Browse Source

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

jrivard 10 năm trước cách đây
mục cha
commit
04da0cd1cb
24 tập tin đã thay đổi với 331 bổ sung125 xóa
  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">
     <target name="makeZIP" depends="makeWAR,javadoc">
         <zip zipfile="${destination}/pwm.zip" filesonly="true">
         <zip zipfile="${destination}/pwm.zip" filesonly="true">
             <zipfileset file="${destination.war}"/>
             <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}/src" prefix="servlet/src"/>
             <zipfileset dir="${src.root}/web" prefix="servlet/web"/>
             <zipfileset dir="${src.root}/web" prefix="servlet/web"/>
             <zipfileset dir="${src.root}/../supplemental" prefix="supplemental"/>
             <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_REFRESH_TOKEN                  ("http.parameter.oauth.refreshToken"),
     HTTP_PARAM_OAUTH_STATE                          ("http.parameter.oauth.state"),
     HTTP_PARAM_OAUTH_STATE                          ("http.parameter.oauth.state"),
     HTTP_PARAM_OAUTH_GRANT_TYPE                     ("http.parameter.oauth.grantType"),
     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_RECYCLE_AT_AUTH                    ("http.session.recycleAtAuth"),
     HTTP_SESSION_VALIDATION_KEY_LENGTH              ("http.session.validationKeyLength"),
     HTTP_SESSION_VALIDATION_KEY_LENGTH              ("http.session.validationKeyLength"),
     LOCALDB_COMPRESSION_ENABLED                     ("localdb.compression.enabled"),
     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.refreshToken=refresh_token
 http.parameter.oauth.state=state
 http.parameter.oauth.state=state
 http.parameter.oauth.grantType=grant_type
 http.parameter.oauth.grantType=grant_type
+http.download.buffer.size=102400
 http.session.recycleAtAuth=true
 http.session.recycleAtAuth=true
 http.session.validationKeyLength=32
 http.session.validationKeyLength=32
 intruder.retentionTimeMS=86400000
 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_STORAGE_METHOD(
             "events.user.storageMethod", PwmSettingSyntax.SELECT, PwmSettingCategory.USER_HISTORY),
             "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(
             "events.ldap.attribute", PwmSettingSyntax.STRING, PwmSettingCategory.USER_HISTORY),
             "events.ldap.attribute", PwmSettingSyntax.STRING, PwmSettingCategory.USER_HISTORY),
     EVENTS_LDAP_MAX_EVENTS(
     EVENTS_LDAP_MAX_EVENTS(

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

@@ -1610,6 +1610,49 @@
             <option value="LDAP">LDAP</option>
             <option value="LDAP">LDAP</option>
         </options>
         </options>
     </setting>
     </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">
     <setting key="events.ldap.attribute" level="2">
         <regex>^[a-zA-Z][a-zA-Z0-9-]*$</regex>
         <regex>^[a-zA-Z][a-zA-Z0-9-]*$</regex>
         <default>
         <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<>();
         final HashMap<String,Object> outputObj = new HashMap<>();
         outputObj.put("settings",settingData);
         outputObj.put("settings",settingData);
-        outputObj.put("template",this.getTemplate().toString());
+        outputObj.put("template", this.getTemplate().toString());
 
 
         return Collections.unmodifiableMap(outputObj);
         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(
     public void writeSetting(
             final PwmSetting setting,
             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);
         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 {
 public enum AuditEvent {
 
 
     // system events
     // 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
     // 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
     // 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 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 Message message;
-    final private boolean storeOnUser;
     private Type type;
     private Type type;
 
 
-    AuditEvent(final Message message, final Type type, boolean storeOnUser) {
+    AuditEvent(final Message message, final Type type) {
         this.message = message;
         this.message = message;
-        this.storeOnUser = storeOnUser;
         this.type = type;
         this.type = type;
     }
     }
 
 
@@ -82,10 +80,6 @@ public enum AuditEvent {
         return message;
         return message;
     }
     }
 
 
-    public boolean isStoreOnUser() {
-        return storeOnUser;
-    }
-
     public static AuditEvent forKey(final String key) {
     public static AuditEvent forKey(final String key) {
         for (final AuditEvent loopEvent : AuditEvent.values()) {
         for (final AuditEvent loopEvent : AuditEvent.values()) {
             final Message message = loopEvent.getMessage();
             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.IOException;
 import java.io.OutputStream;
 import java.io.OutputStream;
-import java.io.Serializable;
 import java.util.*;
 import java.util.*;
 
 
 public class AuditManager implements PwmService {
 public class AuditManager implements PwmService {
     private static final PwmLogger LOGGER = PwmLogger.forClass(AuditManager.class);
     private static final PwmLogger LOGGER = PwmLogger.forClass(AuditManager.class);
 
 
     private STATUS status = STATUS.NEW;
     private STATUS status = STATUS.NEW;
-    private Settings settings = new Settings();
+    private AuditSettings settings;
     private ServiceInfo serviceInfo = new ServiceInfo(Collections.<DataStorageMethod>emptyList());
     private ServiceInfo serviceInfo = new ServiceInfo(Collections.<DataStorageMethod>emptyList());
 
 
     private SyslogAuditService syslogManager;
     private SyslogAuditService syslogManager;
@@ -189,10 +188,7 @@ public class AuditManager implements PwmService {
         this.status = STATUS.OPENING;
         this.status = STATUS.OPENING;
         this.pwmApplication = pwmApplication;
         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) {
         if (pwmApplication.getApplicationMode() == null || pwmApplication.getApplicationMode() == PwmApplication.MODE.READ_ONLY) {
             this.status = STATUS.CLOSED;
             this.status = STATUS.CLOSED;
@@ -317,20 +313,20 @@ public class AuditManager implements PwmService {
         if (record == null || record.getEventCode() == null) {
         if (record == null || record.getEventCode() == null) {
             return;
             return;
         }
         }
-        if (settings.alertFromAddress == null || settings.alertFromAddress.length() < 1) {
+        if (settings.getAlertFromAddress() == null || settings.getAlertFromAddress().length() < 1) {
             return;
             return;
         }
         }
 
 
         switch (record.getEventCode().getType()) {
         switch (record.getEventCode().getType()) {
             case SYSTEM:
             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;
                 break;
 
 
             case USER:
             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;
                 break;
         }
         }
@@ -350,8 +346,7 @@ public class AuditManager implements PwmService {
 
 
         final StringBuilder body = new StringBuilder();
         final StringBuilder body = new StringBuilder();
         final String jsonRecord = JsonUtil.serialize(record);
         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()) {
         for (final String key : mapRecord.keySet()) {
             body.append(key);
             body.append(key);
@@ -396,7 +391,7 @@ public class AuditManager implements PwmService {
             return;
             return;
         }
         }
 
 
-        if (!settings.permittedEvents.contains(auditRecord.getEventCode())) {
+        if (!settings.getPermittedEvents().contains(auditRecord.getEventCode())) {
             LOGGER.debug("discarding event, " + auditRecord.getEventCode() + " are being ignored; event=" + jsonRecord);
             LOGGER.debug("discarding event, " + auditRecord.getEventCode() + " are being ignored; event=" + jsonRecord);
             return;
             return;
         }
         }
@@ -413,8 +408,10 @@ public class AuditManager implements PwmService {
         sendAsEmail(auditRecord);
         sendAsEmail(auditRecord);
 
 
         // add to user ldap record
         // 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
         // send to syslog
@@ -492,13 +489,6 @@ public class AuditManager implements PwmService {
         return counter;
         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()
     public ServiceInfo serviceInfo()
     {
     {
         return serviceInfo;
         return serviceInfo;
@@ -507,11 +497,4 @@ public class AuditManager implements PwmService {
     public int syslogQueueSize() {
     public int syslogQueueSize() {
         return syslogManager != null ? syslogManager.queueSize() : 0;
         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 HttpServletRequest httpServletRequest;
     private final Configuration configuration;
     private final Configuration configuration;
 
 
+    public enum Flag {
+        BypassValidation
+    }
+
     protected PwmHttpRequestWrapper(HttpServletRequest request, final Configuration configuration) {
     protected PwmHttpRequestWrapper(HttpServletRequest request, final Configuration configuration) {
         this.httpServletRequest = request;
         this.httpServletRequest = request;
         this.configuration = configuration;
         this.configuration = configuration;
@@ -95,14 +99,10 @@ public abstract class PwmHttpRequestWrapper {
         return stringValue;
         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
             throws IOException, PwmUnrecoverableException
     {
     {
+        boolean bypassInputValidation = flags != null && Arrays.asList(flags).contains(Flag.BypassValidation);
         final String bodyString = readRequestBodyAsString();
         final String bodyString = readRequestBodyAsString();
         final Map<String, String> inputMap = JsonUtil.deserializeStringMap(bodyString);
         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,
     	NONE,
     	EMAIL,
     	EMAIL,
     	SMS,
     	SMS,
-        ;
-
-        public String getTokenName() {
-            return NewUserServlet.class.getName() + "_" + this.toString();
-        }
     }
     }
 
 
     public Date getCreateStartTime()
     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.UserSearchEngine;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.ldap.auth.SessionAuthenticator;
 import password.pwm.ldap.auth.SessionAuthenticator;
+import password.pwm.token.TokenType;
 import password.pwm.token.TokenPayload;
 import password.pwm.token.TokenPayload;
 import password.pwm.token.TokenService;
 import password.pwm.token.TokenService;
 import password.pwm.util.PostChangePasswordAction;
 import password.pwm.util.PostChangePasswordAction;
@@ -79,7 +80,6 @@ public class ActivateUserServlet extends AbstractPwmServlet {
 // ------------------------------ FIELDS ------------------------------
 // ------------------------------ FIELDS ------------------------------
 
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(ActivateUserServlet.class);
     private static final PwmLogger LOGGER = PwmLogger.forClass(ActivateUserServlet.class);
-    private static final String TOKEN_NAME = ActivateUserServlet.class.getName();
 
 
     public enum ActivateUserAction implements AbstractPwmServlet.ProcessAction {
     public enum ActivateUserAction implements AbstractPwmServlet.ProcessAction {
         activate(HttpMethod.POST),
         activate(HttpMethod.POST),
@@ -622,7 +622,7 @@ public class ActivateUserServlet extends AbstractPwmServlet {
         final String tokenKey;
         final String tokenKey;
         final TokenPayload tokenPayload;
         final TokenPayload tokenPayload;
         try {
         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());
             tokenKey = pwmApplication.getTokenService().generateNewToken(tokenPayload, pwmRequest.getSessionLabel());
             LOGGER.debug(pwmSession.getLabel(), "generated activate user tokenKey code for session");
             LOGGER.debug(pwmSession.getLabel(), "generated activate user tokenKey code for session");
         } catch (PwmOperationalException e) {
         } catch (PwmOperationalException e) {
@@ -649,7 +649,7 @@ public class ActivateUserServlet extends AbstractPwmServlet {
             final TokenPayload tokenPayload = pwmApplication.getTokenService().processUserEnteredCode(
             final TokenPayload tokenPayload = pwmApplication.getTokenService().processUserEnteredCode(
                     pwmSession,
                     pwmSession,
                     activateUserBean.getUserIdentity(),
                     activateUserBean.getUserIdentity(),
-                    TOKEN_NAME,
+                    TokenType.ACTIVATION,
                     userEnteredCode
                     userEnteredCode
             );
             );
             if (tokenPayload != null) {
             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.error.*;
 import password.pwm.health.*;
 import password.pwm.health.*;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.HttpMethod;
+import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.bean.ConfigManagerBean;
 import password.pwm.http.bean.ConfigManagerBean;
@@ -90,6 +91,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
         settingData(HttpMethod.GET),
         settingData(HttpMethod.GET),
         testMacro(HttpMethod.POST),
         testMacro(HttpMethod.POST),
         browseLdap(HttpMethod.POST),
         browseLdap(HttpMethod.POST),
+        copyProfile(HttpMethod.POST),
 
 
         ;
         ;
 
 
@@ -244,7 +246,11 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
                     return;
                     return;
 
 
                 case browseLdap:
                 case browseLdap:
-                    restBrowseLdap(pwmRequest);
+                    restBrowseLdap(pwmRequest, configManagerBean);
+                    return;
+
+                case copyProfile:
+                    restCopyProfile(pwmRequest, configManagerBean);
                     return;
                     return;
             }
             }
         }
         }
@@ -1026,7 +1032,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
 
 
     private void restTestMacro(final PwmRequest pwmRequest) throws IOException, ServletException {
     private void restTestMacro(final PwmRequest pwmRequest) throws IOException, ServletException {
         try {
         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")) {
             if (inputMap == null || !inputMap.containsKey("input")) {
                 pwmRequest.outputJsonResult(new RestResultBean("missing input"));
                 pwmRequest.outputJsonResult(new RestResultBean("missing input"));
                 return;
                 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 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 profile = inputMap.get("profile");
         final String dn = inputMap.containsKey("dn") ? inputMap.get("dn") : "";
         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);
         final LdapBrowser.LdapBrowseResult result = ldapBrowser.doBrowse(profile, dn);
         ldapBrowser.close();
         ldapBrowser.close();
 
 
@@ -1064,5 +1072,30 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
         pwmRequest.outputJsonResult(new RestResultBean(result));
         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.config.value.*;
 import password.pwm.error.*;
 import password.pwm.error.*;
 import password.pwm.health.*;
 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.http.bean.ConfigGuideBean;
 import password.pwm.ldap.LdapBrowser;
 import password.pwm.ldap.LdapBrowser;
 import password.pwm.ldap.schema.SchemaManager;
 import password.pwm.ldap.schema.SchemaManager;
@@ -460,7 +457,7 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
         }
         }
 
 
         final Date startTime = new Date();
         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 profile = inputMap.get("profile");
         final String dn = inputMap.containsKey("dn") ? inputMap.get("dn") : "";
         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");
         resp.setHeader(PwmConstants.HttpHeader.ContentTransferEncoding, "binary");
         final LocalDBUtility localDBUtility = new LocalDBUtility(pwmRequest.getPwmApplication().getLocalDB());
         final LocalDBUtility localDBUtility = new LocalDBUtility(pwmRequest.getPwmApplication().getLocalDB());
         try {
         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());
             LOGGER.debug(pwmRequest, "completed localDBExport process in " + TimeDuration.fromCurrent(startTime).asCompactString());
         } catch (Exception e) {
         } catch (Exception e) {
             LOGGER.error(pwmRequest, "error downloading export localdb: " + e.getMessage());
             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.UserDataReader;
 import password.pwm.ldap.UserSearchEngine;
 import password.pwm.ldap.UserSearchEngine;
 import password.pwm.ldap.auth.SessionAuthenticator;
 import password.pwm.ldap.auth.SessionAuthenticator;
+import password.pwm.token.TokenType;
 import password.pwm.token.TokenPayload;
 import password.pwm.token.TokenPayload;
 import password.pwm.token.TokenService;
 import password.pwm.token.TokenService;
 import password.pwm.util.*;
 import password.pwm.util.*;
@@ -405,7 +406,7 @@ public class NewUserServlet extends AbstractPwmServlet {
                 final NewUserTokenData newUserTokenData = NewUserFormUtils.fromTokenPayload(pwmRequest, tokenPayload);
                 final NewUserTokenData newUserTokenData = NewUserFormUtils.fromTokenPayload(pwmRequest, tokenPayload);
                 newUserBean.setProfileID(newUserTokenData.profileID);
                 newUserBean.setProfileID(newUserTokenData.profileID);
                 final NewUserBean.NewUserForm newUserFormFromToken = newUserTokenData.formData;
                 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");
                     LOGGER.debug(pwmRequest, "email token passed");
 
 
                     try {
                     try {
@@ -421,7 +422,7 @@ public class NewUserServlet extends AbstractPwmServlet {
                     newUserBean.setEmailTokenIssued(true);
                     newUserBean.setEmailTokenIssued(true);
                     newUserBean.setVerificationPhase(NewUserBean.NewUserVerificationPhase.NONE);
                     newUserBean.setVerificationPhase(NewUserBean.NewUserVerificationPhase.NONE);
                     tokenPassed = true;
                     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)) {
                     if (newUserBean.getNewUserForm() != null && newUserBean.getNewUserForm().isConsistentWith(newUserFormFromToken)) {
                         LOGGER.debug(pwmRequest, "SMS token passed");
                         LOGGER.debug(pwmRequest, "SMS token passed");
                         newUserBean.setSmsTokenPassed(true);
                         newUserBean.setSmsTokenPassed(true);
@@ -832,7 +833,7 @@ public class NewUserServlet extends AbstractPwmServlet {
                 final String tokenKey;
                 final String tokenKey;
                 try {
                 try {
                     final TokenPayload tokenPayload = pwmApplication.getTokenService().createTokenPayload(
                     final TokenPayload tokenPayload = pwmApplication.getTokenService().createTokenPayload(
-                            NewUserBean.NewUserVerificationPhase.SMS.getTokenName(),
+                            TokenType.NEWUSER_SMS,
                             tokenPayloadMap,
                             tokenPayloadMap,
                             null,
                             null,
                             Collections.singleton(outputDestTokenData.getSms())
                             Collections.singleton(outputDestTokenData.getSms())
@@ -877,7 +878,7 @@ public class NewUserServlet extends AbstractPwmServlet {
                 final String tokenKey;
                 final String tokenKey;
                 try {
                 try {
                     final TokenPayload tokenPayload = pwmApplication.getTokenService().createTokenPayload(
                     final TokenPayload tokenPayload = pwmApplication.getTokenService().createTokenPayload(
-                            NewUserBean.NewUserVerificationPhase.EMAIL.getTokenName(),
+                            TokenType.NEWUSER_EMAIL,
                             tokenPayloadMap,
                             tokenPayloadMap,
                             null,
                             null,
                             Collections.singleton(outputDestTokenData.getEmail())
                             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.AuthenticationType;
 import password.pwm.ldap.auth.AuthenticationUtility;
 import password.pwm.ldap.auth.AuthenticationUtility;
 import password.pwm.ldap.auth.SessionAuthenticator;
 import password.pwm.ldap.auth.SessionAuthenticator;
+import password.pwm.token.TokenType;
 import password.pwm.token.TokenPayload;
 import password.pwm.token.TokenPayload;
 import password.pwm.token.TokenService;
 import password.pwm.token.TokenService;
 import password.pwm.util.JsonUtil;
 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 PwmLogger LOGGER = PwmLogger.forClass(ForgottenPasswordServlet.class);
 
 
-    private static final String TOKEN_NAME = ForgottenPasswordServlet.class.getName();
-
-
-
     public enum ForgottenPasswordAction implements AbstractPwmServlet.ProcessAction {
     public enum ForgottenPasswordAction implements AbstractPwmServlet.ProcessAction {
         search(HttpMethod.POST),
         search(HttpMethod.POST),
         checkResponses(HttpMethod.POST),
         checkResponses(HttpMethod.POST),
@@ -406,7 +403,7 @@ public class ForgottenPasswordServlet extends AbstractPwmServlet {
             final TokenPayload tokenPayload = pwmRequest.getPwmApplication().getTokenService().processUserEnteredCode(
             final TokenPayload tokenPayload = pwmRequest.getPwmApplication().getTokenService().processUserEnteredCode(
                     pwmRequest.getPwmSession(),
                     pwmRequest.getPwmSession(),
                     forgottenPasswordBean.getUserInfo() == null ? null : forgottenPasswordBean.getUserInfo().getUserIdentity(),
                     forgottenPasswordBean.getUserInfo() == null ? null : forgottenPasswordBean.getUserInfo().getUserIdentity(),
-                    TOKEN_NAME,
+                    TokenType.FORGOTTEN_PW,
                     userEnteredCode
                     userEnteredCode
             );
             );
             if (tokenPayload != null) {
             if (tokenPayload != null) {
@@ -1009,7 +1006,7 @@ public class ForgottenPasswordServlet extends AbstractPwmServlet {
         final String tokenKey;
         final String tokenKey;
         TokenPayload tokenPayload;
         TokenPayload tokenPayload;
         try {
         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());
             tokenKey = pwmRequest.getPwmApplication().getTokenService().generateNewToken(tokenPayload, pwmRequest.getSessionLabel());
         } catch (PwmOperationalException e) {
         } catch (PwmOperationalException e) {
             throw new PwmUnrecoverableException(e.getErrorInformation());
             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.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.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.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_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_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).
 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.maxAge=Maximum Age LocalDB Events
 Setting_Label_events.pwmDB.maxEvents=Maximum LocalDB Events
 Setting_Label_events.pwmDB.maxEvents=Maximum LocalDB Events
 Setting_Label_events.user.storageMethod=User History Storage Location
 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_expireCheckDuringAuth=Check Expire During Authentication
 Setting_Label_expirePreTime=Password Pre-Expire Time
 Setting_Label_expirePreTime=Password Pre-Expire Time
 Setting_Label_expireWarnTime=Password Expire Warn 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 long count = counter++;
         final StringBuilder guid = new StringBuilder();
         final StringBuilder guid = new StringBuilder();
         try {
         try {
@@ -100,7 +100,7 @@ public class TokenService implements PwmService {
         } catch (Exception e) {
         } catch (Exception e) {
             LOGGER.error("error making payload guid: " + e.getMessage(),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)
     public void init(final PwmApplication pwmApplication)
@@ -502,7 +502,7 @@ public class TokenService implements PwmService {
     public TokenPayload processUserEnteredCode(
     public TokenPayload processUserEnteredCode(
             final PwmSession pwmSession,
             final PwmSession pwmSession,
             final UserIdentity sessionUserIdentity,
             final UserIdentity sessionUserIdentity,
-            final String tokenName,
+            final TokenType tokenType,
             final String userEnteredCode
             final String userEnteredCode
     )
     )
             throws PwmOperationalException, PwmUnrecoverableException
             throws PwmOperationalException, PwmUnrecoverableException
@@ -512,7 +512,7 @@ public class TokenService implements PwmService {
                     pwmApplication,
                     pwmApplication,
                     pwmSession,
                     pwmSession,
                     sessionUserIdentity,
                     sessionUserIdentity,
-                    tokenName,
+                    tokenType,
                     userEnteredCode
                     userEnteredCode
             );
             );
             if (tokenPayload.getDest() != null) {
             if (tokenPayload.getDest() != null) {
@@ -547,7 +547,7 @@ public class TokenService implements PwmService {
             final PwmApplication pwmApplication,
             final PwmApplication pwmApplication,
             final PwmSession pwmSession,
             final PwmSession pwmSession,
             final UserIdentity sessionUserIdentity,
             final UserIdentity sessionUserIdentity,
-            final String tokenName,
+            final TokenType tokenType,
             final String userEnteredCode
             final String userEnteredCode
     )
     )
             throws PwmOperationalException, PwmUnrecoverableException
             throws PwmOperationalException, PwmUnrecoverableException
@@ -567,8 +567,8 @@ public class TokenService implements PwmService {
 
 
         LOGGER.trace(pwmSession, "retrieved tokenPayload: " + JsonUtil.serialize(tokenPayload));
         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");
                 final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_TOKEN_INCORRECT,"incorrect token/name format");
                 throw new PwmOperationalException(errorInformation);
                 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() {
         statTimer.schedule(new TimerTask() {
             @Override
             @Override
             public void run() {
             public void run() {
-                final String percentStr;
                 if (showLineCount) {
                 if (showLineCount) {
                     final float percentComplete = (float) exportLineCounter / (float) totalLines;
                     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 {
                 } else {
-                    percentStr = "n/a";
+                    writeStringToOut(debugOutput," exported " + exportLineCounter + " records");
                 }
                 }
-
-                writeStringToOut(debugOutput," exported " + exportLineCounter + " records, " + percentStr + " complete");
             }
             }
         },30 * 1000, 30 * 1000);
         },30 * 1000, 30 * 1000);
 
 
@@ -114,15 +112,15 @@ public class LocalDBUtility {
                     }
                     }
                 }
                 }
             }
             }
+        } catch (IOException e) {
+            writeStringToOut(debugOutput,"IO error during localDB export: " + e.getMessage());
         } finally {
         } 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());
         writeStringToOut(debugOutput, "export complete, exported " + exportLineCounter + " records in " + TimeDuration.fromCurrent(startTime).asLongString());
-        statTimer.cancel();
     }
     }
 
 
     private static void writeStringToOut(final Appendable out, final String string) {
     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("style", "border-width: 0");
     valueRow.setAttribute("id",inputID + "_row");
     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>';
     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';
     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)) {
     if (itemCount > 1 && iteration != (itemCount -1)) {
         rowHtml += '<span id="' + downButtonID + '" class="action-icon fa fa-chevron-down"></span>';
         rowHtml += '<span id="' + downButtonID + '" class="action-icon fa fa-chevron-down"></span>';
     }
     }
     rowHtml += '</td>';
     rowHtml += '</td>';
 
 
     var upButtonID = 'button-' + settingKey + '-' + iteration + '-moveUp';
     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) {
     if (itemCount > 1 && iteration != 0) {
         rowHtml += '<span id="' + upButtonID + '" class="action-icon fa fa-chevron-up"></span>';
         rowHtml += '<span id="' + upButtonID + '" class="action-icon fa fa-chevron-up"></span>';
     }
     }
     rowHtml += '</td>';
     rowHtml += '</td>';
 
 
     var deleteButtonID = 'button-' + settingKey + '-' + iteration + '-delete';
     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'))) {
     if (itemCount > 1 || (!settingInfo['required'] && (syntax != 'PROFILE'))) {
         rowHtml += '<span id="' + deleteButtonID + '" class="delete-row-icon action-icon fa fa-times"></span>';
         rowHtml += '<span id="' + deleteButtonID + '" class="delete-row-icon action-icon fa fa-times"></span>';
     }
     }
     rowHtml += '</td>';
     rowHtml += '</td>';
 
 
+
     valueRow.innerHTML = rowHtml;
     valueRow.innerHTML = rowHtml;
     parentDivElement.appendChild(valueRow);
     parentDivElement.appendChild(valueRow);
 
 
@@ -301,6 +312,30 @@ StringArrayValueHandler.drawRow = function(settingKey, iteration, value, itemCou
         PWM_MAIN.addEventHandler('button-' + inputID,'click',function(){
         PWM_MAIN.addEventHandler('button-' + inputID,'click',function(){
             StringArrayValueHandler.valueHandler(settingKey,iteration);
             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)) {
     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">';
         body += '<div style="text-align: center">';
         if (currentDN && currentDN.length > 0 ) {
         if (currentDN && currentDN.length > 0 ) {
-            body += currentDN;
+            body += '<div class="selectableDN" data-dn="' + currentDN + '"><a><code>' + currentDN + '</code></a></div>';
         } else {
         } else {
-            body += '[root]';
+            body += '<code>[root]</code>';
         }
         }
         body += '</div><br/>';
         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 += '<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>';
         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.showDialog({title:'LDAP Browser',dialogClass:'auto',showOk:false,showClose:true,text:body,loadFunction:function(){
             PWM_MAIN.addEventHandler('button-editDN','click',function(){
             PWM_MAIN.addEventHandler('button-editDN','click',function(){
                 UILibrary.stringEditorDialog({value:currentDN,completeFunction:nextFunction});
                 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(){
             PWM_MAIN.addEventHandler('button-clearDN','click',function(){
                 nextFunction('');
                 nextFunction('');
                 PWM_MAIN.closeWaitDialog();
                 PWM_MAIN.closeWaitDialog();