Browse Source

refactor accountinfo to controlled servlet / bean

Jason Rivard 7 năm trước cách đây
mục cha
commit
302a03a551

+ 0 - 26
server/src/main/java/password/pwm/http/ContextManager.java

@@ -139,13 +139,6 @@ public class ContextManager implements Serializable {
             outputError("unable to set default locale as Java machine default locale: " + e.getMessage());
         }
 
-        final EnvironmentTest[] tests = new EnvironmentTest[]{
-                new JavaVersionCheck(),
-        };
-        for (final EnvironmentTest doTest : tests) {
-            startupErrorInformation = doTest.doTest();
-        }
-
         Configuration configuration = null;
         PwmApplicationMode mode = PwmApplicationMode.ERROR;
 
@@ -349,25 +342,6 @@ public class ContextManager implements Serializable {
         return startupErrorInformation;
     }
 
-    private interface EnvironmentTest {
-        ErrorInformation doTest();
-    }
-
-    private static class JavaVersionCheck implements EnvironmentTest {
-        public ErrorInformation doTest() {
-            String stringVersion = java.lang.System.getProperty("java.version");
-            stringVersion = stringVersion.substring(0, 3);
-            final Float f = Float.valueOf(stringVersion);
-            if (f < PwmConstants.JAVA_MINIMUM_VERSION) {
-                final String errorMsg = "the minimum java version required is Java v" + PwmConstants.JAVA_MINIMUM_VERSION;
-                outputError(errorMsg);
-                LOGGER.fatal(errorMsg);
-                return new ErrorInformation(PwmError.ERROR_APP_UNAVAILABLE, errorMsg);
-            }
-            return null;
-        }
-    }
-
     public int getRestartCount()
     {
         return restartCount;

+ 2 - 0
server/src/main/java/password/pwm/http/PwmRequestAttribute.java

@@ -45,6 +45,8 @@ public enum PwmRequestAttribute {
     FormMobileDevices,
     FormCustomLinks,
 
+    AccountInfo,
+
     SetupResponses_ResponseInfo,
 
     SetupOtp_QrCodeValue,

+ 19 - 0
server/src/main/java/password/pwm/http/bean/DisplayElement.java

@@ -0,0 +1,19 @@
+package password.pwm.http.bean;
+
+import lombok.Value;
+
+import java.io.Serializable;
+
+@Value
+public class DisplayElement implements Serializable {
+    private String key;
+    private Type type;
+    private String label;
+    private String value;
+
+    public enum Type {
+        string,
+        timestamp,
+        number,
+    }
+}

+ 1 - 0
server/src/main/java/password/pwm/http/servlet/PwmServletDefinition.java

@@ -39,6 +39,7 @@ import password.pwm.http.bean.SetupOtpBean;
 import password.pwm.http.bean.SetupResponsesBean;
 import password.pwm.http.bean.ShortcutsBean;
 import password.pwm.http.bean.UpdateProfileBean;
+import password.pwm.http.servlet.accountinfo.AccountInformationServlet;
 import password.pwm.http.servlet.admin.AdminServlet;
 import password.pwm.http.servlet.changepw.PrivateChangePasswordServlet;
 import password.pwm.http.servlet.changepw.PublicChangePasswordServlet;

+ 367 - 0
server/src/main/java/password/pwm/http/servlet/accountinfo/AccountInformationBean.java

@@ -0,0 +1,367 @@
+package password.pwm.http.servlet.accountinfo;
+
+import lombok.Builder;
+import lombok.Value;
+import password.pwm.PwmApplication;
+import password.pwm.config.PwmSetting;
+import password.pwm.config.option.ViewStatusFields;
+import password.pwm.config.profile.PwmPasswordPolicy;
+import password.pwm.config.value.data.FormConfiguration;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.PwmRequest;
+import password.pwm.http.bean.DisplayElement;
+import password.pwm.http.tag.PasswordRequirementsTag;
+import password.pwm.i18n.Display;
+import password.pwm.ldap.UserInfo;
+import password.pwm.svc.event.UserAuditRecord;
+import password.pwm.util.LocaleHelper;
+import password.pwm.util.form.FormUtility;
+import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.TimeDuration;
+import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.macro.MacroMachine;
+
+import java.io.Serializable;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+@Value
+@Builder
+public class AccountInformationBean implements Serializable {
+    private static final PwmLogger LOGGER = PwmLogger.forClass(AccountInformationBean.class);
+
+    @Value
+    public static class ActivityRecord implements Serializable {
+        private Instant timestamp;
+        private String label;
+    }
+
+    private List<DisplayElement> accountInfo;
+    private List<DisplayElement> formData;
+    private List<ActivityRecord> auditData;
+    private List<String> passwordRules;
+
+    static AccountInformationBean makeUserAccountInfoBean(
+            final PwmRequest pwmRequest,
+            final UserInfo userInfo,
+            final Locale locale
+
+    )
+            throws PwmUnrecoverableException
+    {
+        final Instant startTime = Instant.now();
+        final AccountInformationBeanBuilder builder = new AccountInformationBean.AccountInformationBeanBuilder();
+
+        builder.accountInfo(makeAccountInfo(pwmRequest, userInfo, locale));
+        builder.formData(makeFormInfo(pwmRequest, locale));
+        builder.auditData(makeAuditInfo(pwmRequest));
+        builder.passwordRules(makePasswordRules(pwmRequest));
+
+        LOGGER.trace(pwmRequest, "generated account information bean in " + TimeDuration.compactFromCurrent(startTime));
+        return builder.build();
+    }
+
+    private static List<String> makePasswordRules(
+            final PwmRequest pwmRequest)
+            throws PwmUnrecoverableException
+    {
+        final PwmPasswordPolicy pwmPasswordPolicy = pwmRequest.getPwmSession().getUserInfo().getPasswordPolicy();
+        final MacroMachine macroMachine = pwmRequest.getPwmSession().getSessionManager().getMacroMachine(pwmRequest.getPwmApplication());
+        final List<String> rules = PasswordRequirementsTag.getPasswordRequirementsStrings(pwmPasswordPolicy, pwmRequest.getConfig(), pwmRequest.getLocale(), macroMachine);
+        return Collections.unmodifiableList(rules);
+    }
+
+    private static List<ActivityRecord> makeAuditInfo(
+            final PwmRequest pwmRequest
+    ) {
+
+        if (!pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.ACCOUNT_INFORMATION_HISTORY)) {
+            return Collections.emptyList();
+        }
+
+        final List<UserAuditRecord> auditRecords = new ArrayList<>();
+        try {
+            auditRecords.addAll(pwmRequest.getPwmApplication().getAuditManager().readUserHistory(pwmRequest.getPwmSession()));
+        } catch (PwmUnrecoverableException e) {
+            LOGGER.debug(pwmRequest, "error reading audit data for user: " + e.getMessage());
+        }
+
+        final List<ActivityRecord> returnData  = new ArrayList<>();
+        for (final UserAuditRecord userAuditRecord : auditRecords) {
+            returnData.add(new ActivityRecord(
+                    userAuditRecord.getTimestamp(),
+                    userAuditRecord.getEventCode().getLocalizedString(pwmRequest.getConfig(), pwmRequest.getLocale())
+            ));
+        }
+
+        return Collections.unmodifiableList(returnData);
+    }
+
+    private static List<DisplayElement> makeFormInfo(
+            final PwmRequest pwmRequest,
+            final Locale locale
+    )
+            throws PwmUnrecoverableException
+    {
+        final List<DisplayElement> returnData  = new ArrayList<>();
+
+        final List<FormConfiguration> formConfiguration = pwmRequest.getConfig().readSettingAsForm(PwmSetting.ACCOUNT_INFORMATION_VIEW_FORM);
+        if (formConfiguration != null && !formConfiguration.isEmpty()) {
+            final Map<FormConfiguration, List<String>> ldapValues = FormUtility.populateFormMapFromLdap(
+                    formConfiguration,
+                    pwmRequest.getSessionLabel(),
+                    pwmRequest.getPwmSession().getUserInfo(),
+                    FormUtility.Flag.ReturnEmptyValues
+            );
+            for (final Map.Entry<FormConfiguration, List<String>> entry : ldapValues.entrySet()) {
+                final FormConfiguration formConfig = entry.getKey();
+                final List<String> values = entry.getValue();
+
+                final String display = formConfig.isMultivalue()
+                        ? StringUtil.collectionToString(values, ", ")
+                        : values.isEmpty() ? "" : values.iterator().next();
+
+                returnData.add(new DisplayElement(
+                        formConfig.getName(),
+                        DisplayElement.Type.string,
+                        formConfig.getLabel(locale),
+                        display
+                ));
+            }
+        }
+
+        return Collections.unmodifiableList(returnData);
+    }
+
+
+    private static List<DisplayElement> makeAccountInfo(
+            final PwmRequest pwmRequest,
+            final UserInfo userInfo,
+            final Locale locale
+    )
+            throws PwmUnrecoverableException
+    {
+        final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
+        final List<DisplayElement> accountInfo = new ArrayList<>();
+        final DataElementMaker maker = new DataElementMaker(pwmApplication, locale, accountInfo);
+
+        maker.add(
+                ViewStatusFields.Username,
+                Display.Field_Username,
+                userInfo.getUsername()
+        );
+
+        maker.add(
+                ViewStatusFields.UserDN,
+                Display.Field_UserDN,
+                userInfo.getUserIdentity().getUserDN()
+        );
+
+        if (pwmApplication.getConfig().getLdapProfiles().size() > 1) {
+            final String ldapProfileID = userInfo.getUserIdentity().getLdapProfileID();
+            final String value = pwmApplication.getConfig().getLdapProfiles().get(ldapProfileID).getDisplayName(locale);
+            maker.add(
+                    ViewStatusFields.UserDN,
+                    Display.Field_LdapProfile,
+                    value
+            );
+        }
+
+        maker.add(
+                ViewStatusFields.UserEmail,
+                Display.Field_UserEmail,
+                userInfo.getUserEmailAddress()
+        );
+
+        maker.add(
+                ViewStatusFields.UserSMS,
+                Display.Field_UserSMS,
+                userInfo.getUserSmsNumber()
+        );
+
+        maker.add(
+                ViewStatusFields.GUID,
+                Display.Field_UserGUID,
+                userInfo.getUserGuid()
+        );
+
+        maker.add(
+                ViewStatusFields.AccountExpirationTime,
+                Display.Field_AccountExpirationTime,
+                userInfo.getAccountExpirationTime()
+        );
+
+        maker.add(
+                ViewStatusFields.PasswordExpired,
+                Display.Field_PasswordExpired,
+                userInfo.getPasswordStatus().isExpired()
+        );
+
+        maker.add(
+                ViewStatusFields.PasswordPreExpired,
+                Display.Field_PasswordPreExpired,
+                userInfo.getPasswordStatus().isPreExpired()
+        );
+
+        maker.add(
+                ViewStatusFields.PasswordWarnPeriod,
+                Display.Field_PasswordWithinWarningPeriod,
+                userInfo.getPasswordStatus().isWarnPeriod()
+        );
+
+        maker.add(
+                ViewStatusFields.PasswordViolatesPolicy,
+                Display.Field_PasswordViolatesPolicy,
+                userInfo.getPasswordStatus().isViolatesPolicy()
+        );
+
+        maker.add(
+                ViewStatusFields.PasswordSetTime,
+                Display.Field_PasswordSetTime,
+                userInfo.getPasswordLastModifiedTime()
+        );
+
+        {
+            final String value = userInfo.getPasswordLastModifiedTime() != null
+                    ? TimeDuration.fromCurrent(userInfo.getPasswordLastModifiedTime()).asLongString(locale)
+                    : LocaleHelper.getLocalizedMessage(locale, Display.Value_NotApplicable, pwmApplication.getConfig());
+            maker.add(
+                    ViewStatusFields.PasswordSetTimeDelta,
+                    Display.Field_PasswordSetTimeDelta,
+                    value
+            );
+        }
+
+        maker.add(
+                ViewStatusFields.PasswordExpireTime,
+                Display.Field_PasswordExpirationTime,
+                userInfo.getPasswordExpirationTime()
+        );
+
+        maker.add(
+                ViewStatusFields.ResponsesStored,
+                Display.Field_ResponsesStored,
+                userInfo.getResponseInfoBean() != null
+        );
+
+        maker.add(
+                ViewStatusFields.ResponsesStored,
+                Display.Field_ResponsesStored,
+                userInfo.getResponseInfoBean() != null
+        );
+
+        if (userInfo.getResponseInfoBean() != null) {
+            maker.add(
+                    ViewStatusFields.ResponsesTimestamp,
+                    Display.Field_ResponsesTimestamp,
+                    userInfo.getResponseInfoBean().getTimestamp()
+            );
+        }
+
+        if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.OTP_ENABLED)) {
+            maker.add(
+                    ViewStatusFields.OTPStored,
+                    Display.Field_OTP_Stored,
+                    userInfo.getOtpUserRecord() != null
+            );
+
+            if (userInfo.getOtpUserRecord() != null) {
+                maker.add(
+                        ViewStatusFields.OTPTimestamp,
+                        Display.Field_OTP_Timestamp,
+                        userInfo.getOtpUserRecord().getTimestamp()
+                );
+            }
+        }
+
+        maker.add(
+                ViewStatusFields.NetworkAddress,
+                Display.Field_NetworkAddress,
+                pwmRequest.getPwmSession().getSessionStateBean().getSrcAddress()
+        );
+
+        maker.add(
+                ViewStatusFields.NetworkHost,
+                Display.Field_NetworkHost,
+                pwmRequest.getPwmSession().getSessionStateBean().getSrcHostname()
+        );
+
+        maker.add(
+                ViewStatusFields.LogoutURL,
+                Display.Field_LogoutURL,
+                pwmRequest.getLogoutURL()
+        );
+
+        maker.add(
+                ViewStatusFields.ForwardURL,
+                Display.Field_ForwardURL,
+                pwmRequest.getForwardUrl()
+        );
+
+        return Collections.unmodifiableList(accountInfo);
+    }
+
+    private static class DataElementMaker {
+        private final PwmApplication pwmApplication;
+        private final Locale locale;
+        private final List<DisplayElement> list;
+
+        DataElementMaker(final PwmApplication pwmApplication, final Locale locale, final List<DisplayElement> list) {
+            this.pwmApplication = pwmApplication;
+            this.locale = locale;
+            this.list = list;
+        }
+
+        void add(final ViewStatusFields viewStatusField, final Display display, final Instant instant) {
+            final Set<ViewStatusFields> viewStatusFields = pwmApplication.getConfig().readSettingAsOptionList(PwmSetting.ACCOUNT_INFORMATION_VIEW_STATUS_VALUES,ViewStatusFields.class);
+
+            if (!viewStatusFields.contains(viewStatusField)) {
+                return;
+            }
+
+            final String strValue = instant == null
+                    ? LocaleHelper.getLocalizedMessage(locale, Display.Value_NotApplicable, pwmApplication.getConfig())
+                    : JavaHelper.toIsoDate(instant);
+
+            list.add(new DisplayElement(
+                    display.name(),
+                    DisplayElement.Type.timestamp,
+                    LocaleHelper.getLocalizedMessage(locale, display, pwmApplication.getConfig()),
+                    strValue
+            ));
+        }
+
+        void add(final ViewStatusFields viewStatusField, final Display display, final boolean value) {
+            add(viewStatusField, display, LocaleHelper.booleanString(
+                    value,
+                    locale,
+                    pwmApplication.getConfig()
+            ));
+        }
+
+        void add(final ViewStatusFields viewStatusField, final Display display, final String value) {
+            final Set<ViewStatusFields> viewStatusFields = pwmApplication.getConfig().readSettingAsOptionList(PwmSetting.ACCOUNT_INFORMATION_VIEW_STATUS_VALUES,ViewStatusFields.class);
+
+            if (!viewStatusFields.contains(viewStatusField)) {
+                return;
+            }
+
+            final String strValue = StringUtil.isEmpty(value)
+                    ? LocaleHelper.getLocalizedMessage(locale, Display.Value_NotApplicable, pwmApplication.getConfig())
+                    : value;
+
+            list.add(new DisplayElement(
+                    display.name(),
+                    DisplayElement.Type.string,
+                    LocaleHelper.getLocalizedMessage(locale, display, pwmApplication.getConfig()),
+                    strValue
+            ));
+        }
+    }
+}

+ 55 - 24
server/src/main/java/password/pwm/http/servlet/AccountInformationServlet.java → server/src/main/java/password/pwm/http/servlet/accountinfo/AccountInformationServlet.java

@@ -20,12 +20,13 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-package password.pwm.http.servlet;
+package password.pwm.http.servlet.accountinfo;
 
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import password.pwm.PwmConstants;
-import password.pwm.config.value.data.FormConfiguration;
-import password.pwm.util.form.FormUtility;
+import password.pwm.http.HttpMethod;
+import password.pwm.http.ProcessStatus;
+import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.config.PwmSetting;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
@@ -34,14 +35,15 @@ import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.JspUrl;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequestAttribute;
+import password.pwm.http.servlet.ControlledPwmServlet;
 import password.pwm.util.logging.PwmLogger;
+import password.pwm.ws.server.RestResultBean;
 
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import java.io.IOException;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.Collection;
+import java.util.Collections;
 
 @WebServlet(
         name="UserInformationServlet",
@@ -51,28 +53,41 @@ import java.util.Map;
                 PwmConstants.URL_PREFIX_PRIVATE + "/accountinformation.jsp"
         }
 )
-public class AccountInformationServlet extends AbstractPwmServlet {
+public class AccountInformationServlet extends ControlledPwmServlet {
     private static final PwmLogger LOGGER = PwmLogger.forClass(AccountInformationServlet.class);
 
-    @Override
-    protected void processAction(final PwmRequest pwmRequest) throws ServletException, IOException, ChaiUnavailableException, PwmUnrecoverableException {
+    public enum AccountInformationAction implements AbstractPwmServlet.ProcessAction {
+        read(HttpMethod.GET),
 
-        if (!pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.ACCOUNT_INFORMATION_ENABLED)) {
-            pwmRequest.respondWithError(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE));
-            return;
+        ;
+
+        private final HttpMethod method;
+
+        AccountInformationAction(final HttpMethod method)
+        {
+            this.method = method;
         }
 
+        public Collection<HttpMethod> permittedMethods()
+        {
+            return Collections.singletonList(method);
+        }
+    }
+
+    @Override
+    public Class<? extends ProcessAction> getProcessActionsClass() {
+        return AccountInformationAction.class;
+    }
+
+    @Override
+    protected void nextStep(final PwmRequest pwmRequest) throws ServletException, IOException, ChaiUnavailableException, PwmUnrecoverableException {
         try {
-            final List<FormConfiguration> formConfiguration = pwmRequest.getConfig().readSettingAsForm(PwmSetting.ACCOUNT_INFORMATION_VIEW_FORM);
-            if (formConfiguration != null && !formConfiguration.isEmpty()) {
-                final Map<FormConfiguration, List<String>> ldapValues = FormUtility.populateFormMapFromLdap(
-                        formConfiguration,
-                        pwmRequest.getSessionLabel(),
-                        pwmRequest.getPwmSession().getUserInfo(),
-                        FormUtility.Flag.ReturnEmptyValues
-                );
-                pwmRequest.setAttribute(PwmRequestAttribute.FormData, new LinkedHashMap<>(ldapValues));
-            }
+            final AccountInformationBean accountInformationBean = AccountInformationBean.makeUserAccountInfoBean(
+                    pwmRequest,
+                    pwmRequest.getPwmSession().getUserInfo(),
+                    pwmRequest.getLocale()
+            );
+            pwmRequest.setAttribute(PwmRequestAttribute.AccountInfo, accountInformationBean);
         } catch (PwmException e) {
             LOGGER.error(pwmRequest, "error reading user form data: " + e.getMessage());
         }
@@ -80,8 +95,24 @@ public class AccountInformationServlet extends AbstractPwmServlet {
         pwmRequest.forwardToJsp(JspUrl.ACCOUNT_INFORMATION);
     }
 
+    @ActionHandler(action = "read")
+    public ProcessStatus handleReadRequest(final PwmRequest pwmRequest) throws IOException, PwmUnrecoverableException {
+        final AccountInformationBean accountInformationBean = AccountInformationBean.makeUserAccountInfoBean(
+                pwmRequest,
+                pwmRequest.getPwmSession().getUserInfo(),
+                pwmRequest.getLocale()
+        );
+        pwmRequest.outputJsonResult(RestResultBean.withData(accountInformationBean));
+        return ProcessStatus.Halt;
+    }
+
     @Override
-    protected ProcessAction readProcessAction(final PwmRequest request) throws PwmUnrecoverableException {
-        return null;
+    public ProcessStatus preProcessCheck(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ServletException {
+        if (!pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.ACCOUNT_INFORMATION_ENABLED)) {
+            pwmRequest.respondWithError(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE));
+            return ProcessStatus.Halt;
+        }
+
+        return ProcessStatus.Continue;
     }
 }

+ 64 - 77
server/src/main/java/password/pwm/http/servlet/admin/AppDashboardData.java

@@ -32,6 +32,7 @@ import password.pwm.config.option.DataStorageMethod;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.health.HealthRecord;
 import password.pwm.http.ContextManager;
+import password.pwm.http.bean.DisplayElement;
 import password.pwm.i18n.Admin;
 import password.pwm.i18n.Display;
 import password.pwm.svc.PwmService;
@@ -69,14 +70,6 @@ public class AppDashboardData implements Serializable {
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(AppDashboardData.class);
 
-    @Value
-    public static class DataElement implements Serializable {
-        private String key;
-        private Type type;
-        private String label;
-        private String value;
-    }
-
     @Value
     public static class ServiceData implements Serializable {
         private String name;
@@ -103,21 +96,15 @@ public class AppDashboardData implements Serializable {
         private boolean configMatch;
     }
 
-    public enum Type {
-        string,
-        timestamp,
-        number,
-    }
-
     public enum Flag {
         IncludeLocalDbTableSizes,
         ShowThreadData,
     }
 
-    private List<DataElement> about;
+    private List<DisplayElement> about;
     private List<ServiceData> services;
-    private List<DataElement> localDbInfo;
-    private List<DataElement> javaAbout;
+    private List<DisplayElement> localDbInfo;
+    private List<DisplayElement> javaAbout;
     private List<ThreadData> threads;
     private Map<LocalDB.DB, String> localDbSizes;
     private List<NodeData> nodeData;
@@ -169,7 +156,7 @@ public class AppDashboardData implements Serializable {
                 + pwmApplication.getLdapConnectionService().connectionCount();
     }
 
-    private static List<DataElement> makeAboutData(
+    private static List<DisplayElement> makeAboutData(
             final PwmApplication pwmApplication,
             final ContextManager contextManager,
             final Locale locale
@@ -178,58 +165,58 @@ public class AppDashboardData implements Serializable {
         final String NA_VALUE = Display.getLocalizedMessage(locale, Display.Value_NotApplicable, pwmApplication.getConfig());
         final PwmNumberFormat numberFormat = PwmNumberFormat.forLocale(locale);
 
-        final List<DataElement> aboutData = new ArrayList<>();
-        aboutData.add(new DataElement(
+        final List<DisplayElement> aboutData = new ArrayList<>();
+        aboutData.add(new DisplayElement(
                 "appVersion",
-                Type.string,
+                DisplayElement.Type.string,
                 l.forKey("Field_AppVersion", PwmConstants.PWM_APP_NAME),
                 PwmConstants.SERVLET_VERSION
         ));
-        aboutData.add(new DataElement(
+        aboutData.add(new DisplayElement(
                 "currentTime",
-                Type.timestamp,
+                DisplayElement.Type.timestamp,
                 l.forKey("Field_CurrentTime"),
                 JavaHelper.toIsoDate(Instant.now())
         ));
-        aboutData.add(new DataElement(
+        aboutData.add(new DisplayElement(
                 "startupTime",
-                Type.timestamp,
+                DisplayElement.Type.timestamp,
                 l.forKey("Field_StartTime"),
                 JavaHelper.toIsoDate(pwmApplication.getStartupTime())
         ));
-        aboutData.add(new DataElement(
+        aboutData.add(new DisplayElement(
                 "runningDuration",
-                Type.string,
+                DisplayElement.Type.string,
                 l.forKey("Field_UpTime"),
                 TimeDuration.fromCurrent(pwmApplication.getStartupTime()).asLongString(locale)
         ));
-        aboutData.add(new DataElement(
+        aboutData.add(new DisplayElement(
                 "installTime",
-                Type.timestamp,
+                DisplayElement.Type.timestamp,
                 l.forKey("Field_InstallTime"),
                 JavaHelper.toIsoDate(pwmApplication.getInstallTime())
         ));
-        aboutData.add(new DataElement(
+        aboutData.add(new DisplayElement(
                 "siteURL",
-                Type.string,
+                DisplayElement.Type.string,
                 l.forKey("Field_SiteURL"),
                 pwmApplication.getConfig().readSettingAsString(PwmSetting.PWM_SITE_URL)
         ));
-        aboutData.add(new DataElement(
+        aboutData.add(new DisplayElement(
                 "instanceID",
-                Type.string,
+                DisplayElement.Type.string,
                 l.forKey("Field_InstanceID"),
                 pwmApplication.getInstanceID()
         ));
-        aboutData.add(new DataElement(
+        aboutData.add(new DisplayElement(
                 "configRestartCounter",
-                Type.number,
+                DisplayElement.Type.number,
                 "Configuration Restart Counter",
                 contextManager == null ? NA_VALUE : numberFormat.format(contextManager.getRestartCount())
         ));
-        aboutData.add(new DataElement(
+        aboutData.add(new DisplayElement(
                 "chaiApiVersion",
-                Type.string,
+                DisplayElement.Type.string,
                 l.forKey("Field_ChaiAPIVersion"),
                 com.novell.ldapchai.ChaiConstant.CHAI_API_VERSION
         ));
@@ -265,26 +252,26 @@ public class AppDashboardData implements Serializable {
         return Collections.unmodifiableList(new ArrayList<>(returnData.values()));
     }
 
-    private static List<DataElement> makeLocalDbInfo(final PwmApplication pwmApplication, final Locale locale) {
-        final List<DataElement> localDbInfo = new ArrayList<>();
+    private static List<DisplayElement> makeLocalDbInfo(final PwmApplication pwmApplication, final Locale locale) {
+        final List<DisplayElement> localDbInfo = new ArrayList<>();
         final String NA_VALUE = Display.getLocalizedMessage(locale, Display.Value_NotApplicable, pwmApplication.getConfig());
         final PwmNumberFormat numberFormat = PwmNumberFormat.forLocale(locale);
 
-        localDbInfo.add(new DataElement(
+        localDbInfo.add(new DisplayElement(
                 "worlistSize",
-                Type.number,
+                DisplayElement.Type.number,
                 "Word List Dictionary Size",
                 numberFormat.format(pwmApplication.getWordlistManager().size())
         ));
-        localDbInfo.add(new DataElement(
+        localDbInfo.add(new DisplayElement(
                 "seedlistSize",
-                Type.number,
+                DisplayElement.Type.number,
                 "Seed List Dictionary Size",
                 numberFormat.format(pwmApplication.getSeedlistManager().size())
         ));
-        localDbInfo.add(new DataElement(
+        localDbInfo.add(new DisplayElement(
                 "sharedHistorySize",
-                Type.number,
+                DisplayElement.Type.number,
                 "Shared Password History Size",
                 numberFormat.format(pwmApplication.getSharedHistoryManager().size())
         ));
@@ -293,34 +280,34 @@ public class AppDashboardData implements Serializable {
             final String display = oldestEntryAge == null
                     ? NA_VALUE
                     : TimeDuration.fromCurrent(oldestEntryAge).asCompactString();
-            localDbInfo.add(new DataElement(
+            localDbInfo.add(new DisplayElement(
                     "oldestSharedHistory",
-                    Type.string,
+                    DisplayElement.Type.string,
                     "OldestShared Password Entry",
                     display
             ));
         }
-        localDbInfo.add(new DataElement(
+        localDbInfo.add(new DisplayElement(
                 "emailQueueSize",
-                Type.number,
+                DisplayElement.Type.number,
                 "Email Queue Size",
                 numberFormat.format(pwmApplication.getEmailQueue().queueSize())
         ));
-        localDbInfo.add(new DataElement(
+        localDbInfo.add(new DisplayElement(
                 "smsQueueSize",
-                Type.number,
+                DisplayElement.Type.number,
                 "SMS Queue Size",
                 numberFormat.format(pwmApplication.getSmsQueue().queueSize())
         ));
-        localDbInfo.add(new DataElement(
+        localDbInfo.add(new DisplayElement(
                 "sharedHistorySize",
-                Type.number,
+                DisplayElement.Type.number,
                 "Syslog Queue Size",
                 String.valueOf(pwmApplication.getAuditManager().syslogQueueSize())
         ));
-        localDbInfo.add(new DataElement(
+        localDbInfo.add(new DisplayElement(
                 "localAuditRecords",
-                Type.number,
+                DisplayElement.Type.number,
                 "Audit Records",
                 pwmApplication.getAuditManager().sizeToDebugString()
         ));
@@ -329,16 +316,16 @@ public class AppDashboardData implements Serializable {
             final String display = eldestAuditRecord != null
                     ? TimeDuration.fromCurrent(eldestAuditRecord).asLongString()
                     : NA_VALUE;
-            localDbInfo.add(new DataElement(
+            localDbInfo.add(new DisplayElement(
                     "oldestLocalAuditRecords",
-                    Type.string,
+                    DisplayElement.Type.string,
                     "Oldest Audit Record",
                     display
             ));
         }
-        localDbInfo.add(new DataElement(
+        localDbInfo.add(new DisplayElement(
                 "logEvents",
-                Type.number,
+                DisplayElement.Type.number,
                 "Log Events",
                 pwmApplication.getLocalDBLogger().sizeToDebugString()
         ));
@@ -346,9 +333,9 @@ public class AppDashboardData implements Serializable {
             final String display = pwmApplication.getLocalDBLogger() != null && pwmApplication.getLocalDBLogger().getTailDate() != null
                     ? TimeDuration.fromCurrent(pwmApplication.getLocalDBLogger().getTailDate()).asLongString()
                     : NA_VALUE;
-            localDbInfo.add(new DataElement(
+            localDbInfo.add(new DisplayElement(
                     "oldestLogEvents",
-                    Type.string,
+                    DisplayElement.Type.string,
                     "Oldest Log Event",
                     display
             ));
@@ -360,9 +347,9 @@ public class AppDashboardData implements Serializable {
                     ? NA_VALUE
                     : StringUtil.formatDiskSize(FileSystemUtility.getFileDirectorySize(
                     pwmApplication.getLocalDB().getFileLocation()));
-            localDbInfo.add(new DataElement(
+            localDbInfo.add(new DisplayElement(
                     "localDbSizeOnDisk",
-                    Type.string,
+                    DisplayElement.Type.string,
                     "LocalDB Size On Disk",
                     display
             ));
@@ -373,9 +360,9 @@ public class AppDashboardData implements Serializable {
                     : pwmApplication.getLocalDB().getFileLocation() == null
                     ? NA_VALUE
                     : StringUtil.formatDiskSize(FileSystemUtility.diskSpaceRemaining(pwmApplication.getLocalDB().getFileLocation()));
-            localDbInfo.add(new DataElement(
+            localDbInfo.add(new DisplayElement(
                     "localDbFreeSpace",
-                    Type.string,
+                    DisplayElement.Type.string,
                     "LocalDB Free Space",
                     display
             ));
@@ -402,12 +389,12 @@ public class AppDashboardData implements Serializable {
     }
 
 
-    private static List<DataElement> makeAboutJavaData(
+    private static List<DisplayElement> makeAboutJavaData(
             final PwmApplication pwmApplication,
             final Locale locale
     ) {
         final Map<PwmAboutProperty, String> aboutMap = PwmAboutProperty.makeInfoBean(pwmApplication);
-        final List<DataElement> javaInfo = new ArrayList<>();
+        final List<DisplayElement> javaInfo = new ArrayList<>();
         final String NA_VALUE = Display.getLocalizedMessage(locale, Display.Value_NotApplicable, pwmApplication.getConfig());
 
         {
@@ -428,9 +415,9 @@ public class AppDashboardData implements Serializable {
             );
 
             for (final PwmAboutProperty property : interestedProperties) {
-                javaInfo.add(new DataElement(
+                javaInfo.add(new DisplayElement(
                         property.name(),
-                        Type.string,
+                        DisplayElement.Type.string,
                         property.getLabel(),
                         aboutMap.getOrDefault(property, NA_VALUE)
                 ));
@@ -443,17 +430,17 @@ public class AppDashboardData implements Serializable {
             final String display = numberFormat.format(pwmApplication.getResourceServletService().itemsInCache())
                     + "items (" + numberFormat.format(pwmApplication.getResourceServletService().bytesInCache()) + " bytes)";
 
-            javaInfo.add(new DataElement(
+            javaInfo.add(new DisplayElement(
                     "resourceFileServletCacheSize",
-                    Type.string,
+                    DisplayElement.Type.string,
                     "ResourceFileServlet Cache",
                     display
             ));
         }
 
-        javaInfo.add(new DataElement(
+        javaInfo.add(new DisplayElement(
                 "resourceFileServletCacheHitRatio",
-                Type.string,
+                DisplayElement.Type.string,
                 "ResourceFileServlet Cache Hit Ratio",
                 pwmApplication.getResourceServletService().cacheHitRatio().pretty(2)
         ));
@@ -461,16 +448,16 @@ public class AppDashboardData implements Serializable {
         {
             final Map<SessionTrackService.DebugKey, String> debugInfoMap = pwmApplication.getSessionTrackService().getDebugData();
 
-            javaInfo.add(new DataElement(
+            javaInfo.add(new DisplayElement(
                     "sessionTotalSize",
-                    Type.string,
+                    DisplayElement.Type.string,
                     "Estimated Session Total Size",
                     debugInfoMap.get(SessionTrackService.DebugKey.HttpSessionTotalSize)
             ));
 
-            javaInfo.add(new DataElement(
+            javaInfo.add(new DisplayElement(
                     "sessionAverageSize",
-                    Type.string,
+                    DisplayElement.Type.string,
                     "Estimated Session Total Size",
                     debugInfoMap.get(SessionTrackService.DebugKey.HttpSessionAvgSize)
             ));

+ 1 - 1
server/src/main/java/password/pwm/svc/report/UserCacheRecord.java

@@ -104,7 +104,7 @@ public class UserCacheRecord implements Serializable {
 
         this.setHasOtpSecret(userInfo.getOtpUserRecord() != null);
         this.setOtpSecretSetTime(userInfo.getOtpUserRecord() != null && userInfo.getOtpUserRecord().getTimestamp() != null
-                        ? userInfo.getOtpUserRecord().getTimestamp().toInstant()
+                        ? userInfo.getOtpUserRecord().getTimestamp()
                         : null
         );
 

+ 4 - 4
server/src/main/java/password/pwm/util/operations/otp/OTPUserRecord.java

@@ -23,15 +23,15 @@
 package password.pwm.util.operations.otp;
 
 import java.io.Serializable;
+import java.time.Instant;
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.List;
 
 public class OTPUserRecord implements Serializable {
 
     private static final String CURRENT_VERSION = "1";
 
-    private Date timestamp = new Date();
+    private Instant timestamp = Instant.now();
     private String identifier;
     private String secret;
     private List<RecoveryCode> recoveryCodes = new ArrayList<>();
@@ -140,12 +140,12 @@ public class OTPUserRecord implements Serializable {
         this.type = type;
     }
 
-    public Date getTimestamp()
+    public Instant getTimestamp()
     {
         return timestamp;
     }
 
-    public void setTimestamp(final Date timestamp)
+    public void setTimestamp(final Instant timestamp)
     {
         this.timestamp = timestamp;
     }

+ 81 - 379
server/src/main/webapp/WEB-INF/jsp/accountinformation.jsp

@@ -20,409 +20,111 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   --%>
 
-<%@ page import="password.pwm.bean.LocalSessionStateBean" %>
-<%@ page import="password.pwm.bean.ResponseInfoBean" %>
-<%@ page import="password.pwm.ldap.UserInfo" %>
-<%@ page import="password.pwm.config.value.data.FormConfiguration" %>
-<%@ page import="password.pwm.config.PwmSetting" %>
-<%@ page import="password.pwm.config.option.ViewStatusFields" %>
 <%@ page import="password.pwm.http.JspUtility" %>
 <%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.http.servlet.command.CommandServlet" %>
 <%@ page import="password.pwm.i18n.Display" %>
-<%@ page import="password.pwm.svc.event.UserAuditRecord" %>
-<%@ page import="password.pwm.util.LocaleHelper" %>
 <%@ page import="password.pwm.util.java.JavaHelper" %>
 <%@ page import="password.pwm.util.java.StringUtil" %>
-<%@ page import="password.pwm.util.java.TimeDuration" %>
-<%@ page import="java.util.Collections" %>
-<%@ page import="java.util.List" %>
-<%@ page import="java.util.Locale" %>
-<%@ page import="java.util.Map" %>
-<%@ page import="java.util.Set" %>
+<%@ page import="password.pwm.http.servlet.accountinfo.AccountInformationBean" %>
+<%@ page import="password.pwm.http.bean.DisplayElement" %>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
-<%
-    final PwmRequest userinfo_pwmRequest = JspUtility.getPwmRequest(pageContext);
-    final UserInfo userInfo = JspUtility.getPwmRequest(pageContext).getPwmSession().getUserInfo();
-    final LocalSessionStateBean ssBean = userinfo_pwmRequest.getPwmSession().getSessionStateBean();
-    final Set<ViewStatusFields> viewStatusFields = userinfo_pwmRequest.getConfig().readSettingAsOptionList(PwmSetting.ACCOUNT_INFORMATION_VIEW_STATUS_VALUES,ViewStatusFields.class);
-    List<UserAuditRecord> auditRecords = Collections.emptyList();
-    try {
-        auditRecords = userinfo_pwmRequest.getPwmApplication().getAuditManager().readUserHistory(userinfo_pwmRequest.getPwmSession());
-    } catch (Exception e) {
-    /*noop*/
-    }
-    final Locale userLocale = userinfo_pwmRequest.getLocale();
-%>
-
+<% final AccountInformationBean accountInformationBean = (AccountInformationBean)JspUtility.getAttribute(pageContext, PwmRequestAttribute.AccountInfo); %>
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <%@ include file="/WEB-INF/jsp/fragment/header.jsp" %>
 <body class="nihilo">
 <div id="wrapper" class="nihilo">
-<jsp:include page="/WEB-INF/jsp/fragment/header-body.jsp">
-    <jsp:param name="pwm.PageName" value="Title_UserInformation"/>
-</jsp:include>
-<div id="centerbody">
-<div id="page-content-title" style="display: none;"><pwm:display key="Title_UserInformation" displayIfMissing="true"/></div>
-<div data-dojo-type="dijit.layout.TabContainer" style="width: 100%; height: 100%;" data-dojo-props="doLayout: false">
-<div data-dojo-type="dijit.layout.ContentPane" id="UserInformation" title="<pwm:display key="Title_UserInformation"/>" class="tabContent">
-    <table class="nomargin">
-        <% if (viewStatusFields.contains(ViewStatusFields.Username)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_Username"/>
-            </td>
-            <td id="Username">
-                <%= StringUtil.escapeHtml(userInfo.getUsername()) %>
-            </td>
-        </tr>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.UserDN)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_UserDN"/>
-            </td>
-            <td id="UserDN">
-                <%= StringUtil.escapeHtml(userInfo.getUserIdentity().getUserDN()) %>
-            </td>
-        </tr>
-        <% if (userinfo_pwmRequest.getConfig().getLdapProfiles().size() > 1) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_LdapProfile"/>
-            </td>
-            <td id="LdapProfile">
-                <%= StringUtil.escapeHtml(userinfo_pwmRequest.getConfig().getLdapProfiles().get(
-                        userInfo.getUserIdentity().getLdapProfileID()).getDisplayName(
-                        userinfo_pwmRequest.getLocale())) %>
-            </td>
-        </tr>
-        <% } %>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.UserEmail)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_UserEmail"/>
-            </td>
-            <td id="userEmail">
-                <% if (userInfo.getUserEmailAddress() == null) { %>
-                <pwm:display key="Value_NotApplicable"/>
-                <% } else { %>
-                <%= StringUtil.escapeHtml(userInfo.getUserEmailAddress()) %>
-                <% } %>
-            </td>
-        </tr>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.UserSMS)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_UserSMS"/>
-            </td>
-            <td id="UserSMS">
-                <% if (userInfo.getUserSmsNumber() == null) { %>
-                <pwm:display key="Value_NotApplicable"/>
-                <% } else { %>
-                <%= StringUtil.escapeHtml(userInfo.getUserSmsNumber()) %>
-                <% } %>
-            </td>
-        </tr>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.GUID)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_UserGUID"/>
-            </td>
-            <td id="UserGUID">
-                <%= StringUtil.escapeHtml(userInfo.getUserGuid()) %>
-            </td>
-        </tr>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.AccountExpirationTime)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_AccountExpirationTime"/>
-            </td>
-            <% if (userInfo.getAccountExpirationTime() == null) { %>
-            <td id="AccountExpirationTime">
-                <pwm:display key="Value_NotApplicable"/>
-            </td>
-            <% } else { %>
-            <td class="timestamp" id="AccountExpirationTime">
-                <%= JavaHelper.toIsoDate(userInfo.getAccountExpirationTime()) %>
-            </td>
-            <% } %>
-            </td>
-        </tr>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.PasswordExpired)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_PasswordExpired"/>
-            </td>
-            <td id="PasswordExpired">
-                <%if (userInfo.getPasswordStatus().isExpired()) {%><pwm:display key="Value_True"/><% } else { %><pwm:display key="Value_False"/><% } %>
-            </td>
-        </tr>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.PasswordPreExpired)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_PasswordPreExpired"/>
-            </td>
-            <td id="PasswordPreExpired">
-                <%if (userInfo.getPasswordStatus().isPreExpired()) {%><pwm:display key="Value_True"/><% } else { %><pwm:display key="Value_False"/><% } %>
-            </td>
-        </tr>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.PasswordWarnPeriod)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_PasswordWithinWarningPeriod"/>
-            </td>
-            <td id="PasswordWithinWarningPeriod">
-                <%if (userInfo.getPasswordStatus().isWarnPeriod()) { %><pwm:display key="Value_True"/><% } else { %><pwm:display key="Value_False"/><% } %>
-            </td>
-        </tr>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.PasswordViolatesPolicy)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_PasswordViolatesPolicy"/>
-            </td>
-            <td id="PasswordViolatesPolicy">
-                <% if (userInfo.getPasswordStatus().isViolatesPolicy()) {%><pwm:display key="Value_True"/><% } else { %><pwm:display key="Value_False"/><% } %>
-            </td>
-        </tr>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.PasswordSetTime)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_PasswordSetTime"/>
-            </td>
-            <% if (userInfo.getPasswordLastModifiedTime() == null) { %>
-            <td  id="PasswordSetTime">
-                <pwm:display key="Value_NotApplicable"/>
-            </td>
-            <% } else { %>
-            <td class="timestamp"  id="PasswordSetTime">
-                <%= JavaHelper.toIsoDate(userInfo.getPasswordLastModifiedTime()) %>
-            </td>
-            <% } %>
-        </tr>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.PasswordSetTimeDelta)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_PasswordSetTimeDelta"/>
-            </td>
-            <td id="PasswordSetTimeDelta">
-                <%= userInfo.getPasswordLastModifiedTime() != null
-                        ? TimeDuration.fromCurrent(userInfo.getPasswordLastModifiedTime()).asLongString(ssBean.getLocale())
-                        : LocaleHelper.getLocalizedMessage(Display.Value_NotApplicable, userinfo_pwmRequest)
-                %>
-            </td>
-        </tr>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_PasswordExpirationTime"/>
-            </td>
-            <% if (userInfo.getPasswordExpirationTime() == null) { %>
-            <td id="PasswordExpirationTime">
-                <pwm:display key="Value_NotApplicable"/>
-            </td>
-            <% } else { %>
-            <td class="timestamp" id="PasswordExpirationTime">
-                <%= JavaHelper.toIsoDate(userInfo.getPasswordExpirationTime()) %>
-            </td>
-            <% } %>
-        </tr>
-        <% } %>
-        <% final ResponseInfoBean responseInfoBean = userinfo_pwmRequest.getPwmSession().getUserInfo().getResponseInfoBean(); %>
-        <% if (viewStatusFields.contains(ViewStatusFields.ResponsesStored)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_ResponsesStored"/>
-            </td>
-            <td id="ResponsesStored">
-                <%if (!userInfo.isRequiresResponseConfig()) { %><pwm:display key="Value_True"/><% } else { %><pwm:display key="Value_False"/><% } %>
-            </td>
-        </tr>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.ResponsesTimestamp)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_ResponsesTimestamp"/>
-            </td>
-            <% if (responseInfoBean == null || responseInfoBean.getTimestamp() == null ) { %>
-            <td id="ResponsesTimestamp">
-                <pwm:display key="Value_NotApplicable"/>
-            </td>
-            <% } else { %>
-            <td class="timestamp" id="ResponsesTimestamp">
-                <%= JavaHelper.toIsoDate(responseInfoBean.getTimestamp()) %>
-            </td>
-            <% } %>
-        </tr>
-        <% } %>
-        <pwm:if test="<%=PwmIfTest.otpEnabled%>">
-            <% if (viewStatusFields.contains(ViewStatusFields.OTPStored)) { %>
-            <tr>
-                <td class="key">
-                    <pwm:display key="Field_OTP_Stored"/>
-                </td>
-                <td id="OTP_Stored">
-                    <%if (userInfo.getOtpUserRecord() != null) {%><pwm:display key="Value_True"/><% } else { %><pwm:display key="Value_False"/><% } %>
-                </td>
-            </tr>
-            <% } %>
-            <% if (viewStatusFields.contains(ViewStatusFields.OTPTimestamp)) { %>
-            <tr>
-                <td class="key">
-                    <pwm:display key="Field_OTP_Timestamp"/>
-                </td>
-                <% if (userInfo.getOtpUserRecord() == null || userInfo.getOtpUserRecord().getTimestamp() == null) { %>
-                <td id="OPT_Timestamp">
-                    <pwm:display key="Value_NotApplicable"/>
-                </td>
-                <% } else { %>
-                <td class="timestamp" id="OPT_Timestamp">
-                    <%= JavaHelper.toIsoDate(userInfo.getOtpUserRecord().getTimestamp()) %>
-                </td>
-                <% } %>
-            </tr>
-            <% } %>
-        </pwm:if>
-        <% if (viewStatusFields.contains(ViewStatusFields.NetworkAddress)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_NetworkAddress"/>
-            </td>
-            <td id="NetworkAddress">
-                <%= ssBean.getSrcAddress() %>
-            </td>
-        </tr>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.NetworkHost)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_NetworkHost"/>
-            </td>
-            <td id="NetworkHost">
-                <%= ssBean.getSrcHostname() %>
-            </td>
-        </tr>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.LogoutURL)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_LogoutURL"/>
-            </td>
-            <td id="LogoutURL">
-                <%= StringUtil.escapeHtml(userinfo_pwmRequest.getLogoutURL()) %>
-            </td>
-        </tr>
-        <% } %>
-        <% if (viewStatusFields.contains(ViewStatusFields.ForwardURL)) { %>
-        <tr>
-            <td class="key">
-                <pwm:display key="Field_ForwardURL"/>
-            </td>
-            <td id="ForwardURL">
-                <%= StringUtil.escapeHtml(userinfo_pwmRequest.getForwardUrl()) %>
-            </td>
-        </tr>
-        <% } %>
-    </table>
-</div>
-<% final Map<FormConfiguration, List<String>> userFormData = (Map<FormConfiguration,List<String>>)JspUtility.getAttribute(pageContext, PwmRequestAttribute.FormData); %>
-<% if (userFormData != null && !userFormData.isEmpty()) { %>
-<div data-dojo-type="dijit.layout.ContentPane" id="UserData" title="<pwm:display key="<%=Display.Title_UserData.toString()%>"/>" class="tabContent">
-    <div style="max-height: 400px; overflow: auto;">
-        <table class="nomargin">
-            <% for (final FormConfiguration formConfiguration : userFormData.keySet()) { %>
-            <tr>
-                <td class="key" style="width:50%">
-                    <span class="timestamp">
-                    <%= formConfiguration.getLabel(userLocale) %>
-                    </span>
-                </td>
-                <td>
-                    <% for (final String value : userFormData.get(formConfiguration)) { %>
-                    <%=  StringUtil.escapeHtml(value) %><br/>
+    <jsp:include page="/WEB-INF/jsp/fragment/header-body.jsp">
+        <jsp:param name="pwm.PageName" value="Title_UserInformation"/>
+    </jsp:include>
+    <div id="centerbody">
+        <div id="page-content-title" style="display: none;"><pwm:display key="Title_UserInformation" displayIfMissing="true"/></div>
+        <div data-dojo-type="dijit.layout.TabContainer" style="width: 100%; height: 100%;" data-dojo-props="doLayout: false">
+            <div data-dojo-type="dijit.layout.ContentPane" id="UserInformation" title="<pwm:display key="Title_UserInformation"/>" class="tabContent">
+                <table class="nomargin">
+                    <% for (final DisplayElement displayElement : accountInformationBean.getAccountInfo()) { %>
+                    <% request.setAttribute("displayElement", displayElement); %>
+                    <jsp:include page="fragment/displayelement-row.jsp"/>
                     <% } %>
-                </td>
-            </tr>
+                </table>
+            </div>
+            <% if (!JavaHelper.isEmpty(accountInformationBean.getFormData())) { %>
+            <div data-dojo-type="dijit.layout.ContentPane" id="UserData" title="<pwm:display key="<%=Display.Title_UserData.toString()%>"/>" class="tabContent">
+                <div style="max-height: 400px; overflow: auto;">
+                    <table class="nomargin">
+                        <% for (final DisplayElement displayElement : accountInformationBean.getFormData()) { %>
+                        <% request.setAttribute("displayElement", displayElement); %>
+                        <jsp:include page="fragment/displayelement-row.jsp"/>
+                        <% } %>
+                    </table>
+                </div>
+            </div>
             <% } %>
-        </table>
-    </div>
-</div>
-<% } %>
-
-
-
-    <div data-dojo-type="dijit.layout.ContentPane" id="PasswordPolicy" title="<pwm:display key="Title_PasswordPolicy"/>" class="tabContent">
-    <div style="max-height: 400px; overflow: auto;">
-        <table class="nomargin">
-            <tr>
-                <td class="key">
-                    <pwm:display key="Title_PasswordPolicy"/>
-                </td>
-                <td id="PasswordRequirments">
-                    <ul>
-                        <pwm:DisplayPasswordRequirements separator="</li>" prepend="<li>"/>
-                    </ul>
-                </td>
-            </tr>
-        </table>
-    </div>
-</div>
-<% if (userinfo_pwmRequest != null && ContextManager.getPwmApplication(session).getConfig().readSettingAsBoolean(PwmSetting.ACCOUNT_INFORMATION_HISTORY)) { %>
-<% if (auditRecords != null && !auditRecords.isEmpty()) { %>
-<div data-dojo-type="dijit.layout.ContentPane" id="UserEventHistory" title="<pwm:display key="Title_UserEventHistory"/>" class="tabContent">
-    <div style="max-height: 400px; overflow: auto;">
-        <table class="nomargin">
-            <% for (final UserAuditRecord record : auditRecords) { %>
-            <tr>
-                <td class="key" style="width:50%">
+            <% if (!JavaHelper.isEmpty(accountInformationBean.getPasswordRules())) { %>
+            <div data-dojo-type="dijit.layout.ContentPane" id="PasswordPolicy" title="<pwm:display key="Title_PasswordPolicy"/>" class="tabContent">
+                <div style="max-height: 400px; overflow: auto;">
+                    <table class="nomargin">
+                        <tr>
+                            <td class="key">
+                                <pwm:display key="Title_PasswordPolicy"/>
+                            </td>
+                            <td id="PasswordRequirements">
+                                <ul>
+                                    <% for (final String rule : accountInformationBean.getPasswordRules()) { %>
+                                    <li><%=  StringUtil.escapeHtml(rule) %></li>
+                                    <% } %>
+                                </ul>
+                            </td>
+                        </tr>
+                    </table>
+                </div>
+            </div>
+            <% } %>
+            <% if (!JavaHelper.isEmpty(accountInformationBean.getAuditData())) {%>
+            <div data-dojo-type="dijit.layout.ContentPane" id="UserEventHistory" title="<pwm:display key="Title_UserEventHistory"/>" class="tabContent">
+                <div style="max-height: 400px; overflow: auto;">
+                    <table class="nomargin">
+                        <% for (final AccountInformationBean.ActivityRecord record : accountInformationBean.getAuditData()) { %>
+                        <tr>
+                            <td class="key" style="width:50%">
                             <span class="timestamp">
                             <%= JavaHelper.toIsoDate(record.getTimestamp()) %>
                             </span>
-                </td>
-                <td>
-                    <%= record.getEventCode().getLocalizedString(ContextManager.getPwmApplication(session).getConfig(),userLocale) %>
-                </td>
-            </tr>
+                            </td>
+                            <td>
+                                <%= StringUtil.escapeHtml(record.getLabel()) %>
+                            </td>
+                        </tr>
+                        <% } %>
+                    </table>
+                </div>
+            </div>
             <% } %>
-        </table>
+        </div>
+        <div class="buttonbar">
+            <form action="<pwm:url url='<%=PwmServletDefinition.PublicCommand.servletUrl()%>' addContext="true"/>" method="post" enctype="application/x-www-form-urlencoded">
+                <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=CommandServlet.CommandAction.next.toString()%>"/>
+                <input type="hidden" id="pwmFormID" name="pwmFormID" value="<pwm:FormID/>"/>
+                <button type="submit" name="button" class="btn" id="button_continue">
+                    <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
+                    <pwm:display key="Button_Continue"/>
+                </button>
+            </form>
+        </div>
     </div>
-</div>
-<% } %>
-<% } %>
-</div>
-<div class="buttonbar">
-    <form action="<pwm:url url='<%=PwmServletDefinition.PublicCommand.servletUrl()%>' addContext="true"/>" method="post" enctype="application/x-www-form-urlencoded">
-        <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=CommandServlet.CommandAction.next.toString()%>"/>
-        <input type="hidden" id="pwmFormID" name="pwmFormID" value="<pwm:FormID/>"/>
-        <button type="submit" name="button" class="btn" id="button_continue">
-            <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
-            <pwm:display key="Button_Continue"/>
-        </button>
-    </form>
-</div>
-</div>
-<div class="push"></div>
+    <div class="push"></div>
 </div>
 <pwm:script>
-<script type="text/javascript">
-    PWM_GLOBAL['startupFunctions'].push(function(){
-        require(["dojo/parser","dijit/layout/TabContainer","dijit/layout/ContentPane"],function(dojoParser){
-            dojoParser.parse();
+    <script type="text/javascript">
+        PWM_GLOBAL['startupFunctions'].push(function(){
+            require(["dojo/parser","dijit/layout/TabContainer","dijit/layout/ContentPane"],function(dojoParser){
+                dojoParser.parse();
+            });
         });
-    });
-</script>
+    </script>
 </pwm:script>
 <jsp:include page="/WEB-INF/jsp/fragment/footer.jsp"/>
 </body>

+ 10 - 45
server/src/main/webapp/WEB-INF/jsp/admin-dashboard.jsp

@@ -35,6 +35,7 @@
 <%@ page import="password.pwm.svc.stats.EpsStatistic" %>
 <%@ page import="password.pwm.http.servlet.admin.AppDashboardData" %>
 <%@ page import="password.pwm.http.PwmRequestAttribute" %>
+<%@ page import="password.pwm.http.bean.DisplayElement" %>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true"
          contentType="text/html" %>
@@ -162,21 +163,9 @@
             <div id="AboutTab" data-dojo-type="dijit.layout.ContentPane" title="<pwm:display key="Title_About" bundle="Admin"/>" class="tabContent">
                 <div style="max-height: 400px; overflow: auto;">
                     <table class="nomargin">
-                        <% for (final AppDashboardData.DataElement dataElement : appDashboardData.getAbout()) { %>
-                        <tr>
-                            <td class="key">
-                                <%= dataElement.getLabel() %>
-                            </td>
-                            <% if (dataElement.getType() == AppDashboardData.Type.timestamp) { %>
-                            <td class="timestamp">
-                                <%= StringUtil.escapeHtml(dataElement.getValue()) %>
-                            </td>
-                            <% } else { %>
-                            <td>
-                                <%= StringUtil.escapeHtml(dataElement.getValue()) %>
-                            </td>
-                            <% } %>
-                        </tr>
+                        <% for (final DisplayElement displayElement : appDashboardData.getAbout()) { %>
+                        <% request.setAttribute("displayElement", displayElement); %>
+                        <jsp:include page="fragment/displayelement-row.jsp"/>
                         <% } %>
                         <tr>
                             <td class="key">
@@ -280,21 +269,9 @@
             <div id="LocalDBTab" data-dojo-type="dijit.layout.ContentPane" title="LocalDB" class="tabContent">
                 <div style="max-height: 400px; overflow: auto;">
                     <table class="nomargin">
-                        <% for (final AppDashboardData.DataElement dataElement : appDashboardData.getLocalDbInfo()) { %>
-                        <tr>
-                            <td class="key">
-                                <%= dataElement.getLabel() %>
-                            </td>
-                            <% if (dataElement.getType() == AppDashboardData.Type.timestamp) { %>
-                            <td class="timestamp">
-                                <%= StringUtil.escapeHtml(dataElement.getValue()) %>
-                            </td>
-                            <% } else { %>
-                            <td>
-                                <%= StringUtil.escapeHtml(dataElement.getValue()) %>
-                            </td>
-                            <% } %>
-                        </tr>
+                        <% for (final DisplayElement displayElement : appDashboardData.getLocalDbInfo()) { %>
+                        <% request.setAttribute("displayElement", displayElement); %>
+                        <jsp:include page="fragment/displayelement-row.jsp"/>
                         <% } %>
                     </table>
                 </div>
@@ -330,21 +307,9 @@
             </div>
             <div id="JavaTab" data-dojo-type="dijit.layout.ContentPane" title="Java" class="tabContent">
                 <table class="nomargin">
-                    <% for (final AppDashboardData.DataElement dataElement : appDashboardData.getJavaAbout()) { %>
-                    <tr>
-                        <td class="key">
-                            <%= dataElement.getLabel() %>
-                        </td>
-                        <% if (dataElement.getType() == AppDashboardData.Type.timestamp) { %>
-                        <td class="timestamp">
-                            <%= StringUtil.escapeHtml(dataElement.getValue()) %>
-                        </td>
-                        <% } else { %>
-                        <td>
-                            <%= StringUtil.escapeHtml(dataElement.getValue()) %>
-                        </td>
-                        <% } %>
-                    </tr>
+                    <% for (final DisplayElement displayElement : appDashboardData.getJavaAbout()) { %>
+                    <% request.setAttribute("displayElement", displayElement); %>
+                    <jsp:include page="fragment/displayelement-row.jsp"/>
                     <% } %>
                 </table>
                 <br/>

+ 39 - 0
server/src/main/webapp/WEB-INF/jsp/fragment/displayelement-row.jsp

@@ -0,0 +1,39 @@
+<%@ page import="password.pwm.util.java.StringUtil" %>
+<%@ page import="password.pwm.http.bean.DisplayElement" %><%--
+  ~ Password Management Servlets (PWM)
+  ~ http://www.pwm-project.org
+  ~
+  ~ Copyright (c) 2006-2009 Novell, Inc.
+  ~ Copyright (c) 2009-2017 The PWM Project
+  ~
+  ~ This program is free software; you can redistribute it and/or modify
+  ~ it under the terms of the GNU General Public License as published by
+  ~ the Free Software Foundation; either version 2 of the License, or
+  ~ (at your option) any later version.
+  ~
+  ~ This program is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  ~ GNU General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with this program; if not, write to the Free Software
+  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+  --%>
+
+<%@ taglib uri="pwm" prefix="pwm" %>
+<% final DisplayElement displayElement = (DisplayElement)request.getAttribute("displayElement"); %>
+<tr>
+    <td class="key">
+        <%= displayElement.getLabel() %>
+    </td>
+    <% if (displayElement.getType() == DisplayElement.Type.timestamp) { %>
+    <td class="timestamp">
+        <%= StringUtil.escapeHtml(displayElement.getValue()) %>
+    </td>
+    <% } else { %>
+    <td>
+        <%= StringUtil.escapeHtml(displayElement.getValue()) %>
+    </td>
+    <% } %>
+</tr>