Selaa lähdekoodia

helpdesk detail bean refactoring

Jason Rivard 7 vuotta sitten
vanhempi
commit
c458b9c36e

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

@@ -53,7 +53,6 @@ public enum PwmRequestAttribute {
 
     HelpdeskDetail,
     HelpdeskObfuscatedDN,
-    HelpdeskUsername,
     HelpdeskVerificationEnabled,
 
     ConfigFilename,

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

@@ -1,19 +1,36 @@
 package password.pwm.http.bean;
 
-import lombok.Value;
+import lombok.Getter;
 
 import java.io.Serializable;
+import java.util.List;
 
-@Value
+@Getter
 public class DisplayElement implements Serializable {
     private String key;
     private Type type;
     private String label;
     private String value;
+    private List<String> values;
 
     public enum Type {
         string,
         timestamp,
         number,
+        multiString,
+    }
+
+    public DisplayElement(final String key, final Type type, final String label, final String value) {
+        this.key = key;
+        this.type = type;
+        this.label = label;
+        this.value = value;
+    }
+
+    public DisplayElement(final String key, final Type type, final String label, final List<String> values) {
+        this.key = key;
+        this.type = type;
+        this.label = label;
+        this.values = values;
     }
 }

+ 24 - 238
server/src/main/java/password/pwm/http/servlet/accountinfo/AccountInformationBean.java

@@ -3,6 +3,7 @@ package password.pwm.http.servlet.accountinfo;
 import lombok.Builder;
 import lombok.Value;
 import password.pwm.PwmApplication;
+import password.pwm.bean.SessionLabel;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.option.ViewStatusFields;
 import password.pwm.config.profile.PwmPasswordPolicy;
@@ -11,12 +12,10 @@ 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.ldap.ViewableUserInfoDisplayReader;
 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;
@@ -29,7 +28,6 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Set;
 
 @Value
 @Builder
@@ -58,9 +56,20 @@ public class AccountInformationBean implements Serializable {
         final Instant startTime = Instant.now();
         final AccountInformationBeanBuilder builder = new AccountInformationBean.AccountInformationBeanBuilder();
 
-        builder.accountInfo(makeAccountInfo(pwmRequest, userInfo, locale));
+        builder.accountInfo(ViewableUserInfoDisplayReader.makeDisplayData(
+                pwmRequest.getConfig().readSettingAsOptionList(PwmSetting.ACCOUNT_INFORMATION_VIEW_STATUS_VALUES,ViewStatusFields.class),
+                pwmRequest.getConfig(),
+                userInfo,
+                pwmRequest.getPwmSession().getSessionStateBean(),
+                locale
+        ));
         builder.formData(makeFormInfo(pwmRequest, locale));
-        builder.auditData(makeAuditInfo(pwmRequest));
+        builder.auditData(makeAuditInfo(
+                pwmRequest.getPwmApplication(),
+                pwmRequest.getSessionLabel(),
+                userInfo,
+                pwmRequest.getLocale()
+                ));
         builder.passwordRules(makePasswordRules(pwmRequest));
 
         LOGGER.trace(pwmRequest, "generated account information bean in " + TimeDuration.compactFromCurrent(startTime));
@@ -77,26 +86,29 @@ public class AccountInformationBean implements Serializable {
         return Collections.unmodifiableList(rules);
     }
 
-    private static List<ActivityRecord> makeAuditInfo(
-            final PwmRequest pwmRequest
+    public static List<ActivityRecord> makeAuditInfo(
+            final PwmApplication pwmApplication,
+            final SessionLabel sessionLabel,
+            final UserInfo userInfo,
+            final Locale locale
     ) {
 
-        if (!pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.ACCOUNT_INFORMATION_HISTORY)) {
+        if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.ACCOUNT_INFORMATION_HISTORY)) {
             return Collections.emptyList();
         }
 
         final List<UserAuditRecord> auditRecords = new ArrayList<>();
         try {
-            auditRecords.addAll(pwmRequest.getPwmApplication().getAuditManager().readUserHistory(pwmRequest.getPwmSession()));
+            auditRecords.addAll(pwmApplication.getAuditManager().readUserHistory(userInfo));
         } catch (PwmUnrecoverableException e) {
-            LOGGER.debug(pwmRequest, "error reading audit data for user: " + e.getMessage());
+            LOGGER.debug(sessionLabel, "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())
+                    userAuditRecord.getEventCode().getLocalizedString(pwmApplication.getConfig(), locale)
             ));
         }
 
@@ -138,230 +150,4 @@ public class AccountInformationBean implements Serializable {
 
         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
-            ));
-        }
-    }
 }

+ 198 - 64
server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskDetailInfoBean.java

@@ -24,24 +24,32 @@ package password.pwm.http.servlet.helpdesk;
 
 import com.novell.ldapchai.ChaiPasswordRule;
 import com.novell.ldapchai.ChaiUser;
+import com.novell.ldapchai.cr.Challenge;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Data;
 import lombok.Getter;
 import lombok.Setter;
+
 import password.pwm.bean.ResponseInfoBean;
 import password.pwm.bean.UserIdentity;
-import password.pwm.bean.pub.PublicUserInfoBean;
 import password.pwm.config.PwmSetting;
+import password.pwm.config.option.HelpdeskUIMode;
+import password.pwm.config.option.ViewStatusFields;
 import password.pwm.config.profile.HelpdeskProfile;
 import password.pwm.config.profile.PwmPasswordRule;
+import password.pwm.config.value.data.ActionConfiguration;
 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.servlet.accountinfo.AccountInformationBean;
 import password.pwm.http.tag.PasswordRequirementsTag;
 import password.pwm.i18n.Display;
 import password.pwm.ldap.UserInfo;
 import password.pwm.ldap.UserInfoFactory;
-import password.pwm.svc.event.UserAuditRecord;
+import password.pwm.ldap.ViewableUserInfoDisplayReader;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.form.FormUtility;
 import password.pwm.util.java.JavaHelper;
@@ -50,52 +58,64 @@ import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroMachine;
 
-import javax.servlet.ServletException;
-import java.io.IOException;
 import java.io.Serializable;
 import java.time.Instant;
+import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Date;
 import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 
 @Getter
 @Setter(AccessLevel.PRIVATE)
 public class HelpdeskDetailInfoBean implements Serializable {
     private static final PwmLogger LOGGER = PwmLogger.forClass(HelpdeskDetailInfoBean.class);
 
-    private PublicUserInfoBean userInfo;
     private String userDisplayName;
 
-    private boolean intruderLocked;
-    private boolean accountEnabled;
-    private boolean accountExpired;
-
-    private Instant lastLoginTime;
-    private List<UserAuditRecord> userHistory;
-    private Map<FormConfiguration, List<String>> searchDetails;
-    private String passwordSetDelta;
+    private List<AccountInformationBean.ActivityRecord> userHistory;
 
     private Map<String, String> passwordPolicyRules;
     private List<String> passwordRequirements;
     private String passwordPolicyDN;
     private String passwordPolicyID;
 
-    private boolean hasOtpRecord;
-    private String otpRecordTimestamp;
+    private List<DisplayElement> statusData;
+    private List<DisplayElement> profileData;
+    private List<DisplayElement> helpdeskResponses;
+
+    private Set<StandardButton> visibleButtons;
+    private Set<StandardButton> enabledButtons;
+    private List<ButtonInfo> customButtons;
 
-    private ResponseInfoBean responseInfoBean;
+    @Data
+    @AllArgsConstructor
+    public static class ButtonInfo implements Serializable {
+        private String name;
+        private String label;
+        private String description;
+    }
 
-    private transient UserInfo backingUserInfo;
+    public enum StandardButton {
+        back,
+        refresh,
+        changePassword,
+        unlock,
+        clearResponses,
+        clearOtpSecret,
+        verification,
+        deleteUser,
+    }
 
     static HelpdeskDetailInfoBean makeHelpdeskDetailInfo(
             final PwmRequest pwmRequest,
             final HelpdeskProfile helpdeskProfile,
             final UserIdentity userIdentity
     )
-            throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException
+            throws PwmUnrecoverableException, ChaiUnavailableException
     {
         final Instant startTime = Instant.now();
         LOGGER.trace(pwmRequest, "beginning to assemble detail data report for user " + userIdentity);
@@ -116,50 +136,43 @@ public class HelpdeskDetailInfoBean implements Serializable {
         );
         final MacroMachine macroMachine = new MacroMachine(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel(), userInfo, null);
 
-        detailInfo.setUserInfo(PublicUserInfoBean.fromUserInfoBean(userInfo, pwmRequest.getConfig(), pwmRequest.getLocale(), macroMachine));
-
-        try {
-            detailInfo.setIntruderLocked(theUser.isPasswordLocked());
-        } catch (Exception e) {
-            LOGGER.error(pwmRequest, "unexpected error reading intruder lock status for user '" + userIdentity + "', " + e.getMessage());
-        }
-
-        try {
-            detailInfo.setAccountEnabled(theUser.isAccountEnabled());
-        } catch (Exception e) {
-            LOGGER.error(pwmRequest, "unexpected error reading account enabled status for user '" + userIdentity + "', " + e.getMessage());
-        }
-
-        try {
-            detailInfo.setAccountExpired(theUser.isAccountExpired());
-        } catch (Exception e) {
-            LOGGER.error(pwmRequest, "unexpected error reading account expired status for user '" + userIdentity + "', " + e.getMessage());
-        }
-
-        try {
-            final Date lastLoginTime = theUser.readLastLoginTime();
-            detailInfo.setLastLoginTime(lastLoginTime == null ? null : lastLoginTime.toInstant());
-        } catch (Exception e) {
-            LOGGER.error(pwmRequest, "unexpected error reading last login time for user '" + userIdentity + "', " + e.getMessage());
-        }
-
         try {
-            detailInfo.setUserHistory(pwmRequest.getPwmApplication().getAuditManager().readUserHistory(userInfo));
+            detailInfo.userHistory = AccountInformationBean.makeAuditInfo(
+                    pwmRequest.getPwmApplication(),
+                    pwmRequest.getSessionLabel(),
+                    userInfo,
+                    pwmRequest.getLocale()
+            );
         } catch (Exception e) {
             LOGGER.error(pwmRequest, "unexpected error reading userHistory for user '" + userIdentity + "', " + e.getMessage());
         }
 
-        if (detailInfo.getUserInfo().getPasswordLastModifiedTime() != null) {
-            final TimeDuration passwordSetDelta = TimeDuration.fromCurrent(detailInfo.getUserInfo().getPasswordLastModifiedTime());
-            detailInfo.setPasswordSetDelta(passwordSetDelta.asLongString(pwmRequest.getLocale()));
-        } else {
-            detailInfo.setPasswordSetDelta(LocaleHelper.getLocalizedMessage(Display.Value_NotApplicable, pwmRequest));
-        }
-
         {
             final List<FormConfiguration> detailFormConfig = helpdeskProfile.readSettingAsForm(PwmSetting.HELPDESK_DETAIL_FORM);
             final Map<FormConfiguration, List<String>> formData = FormUtility.populateFormMapFromLdap(detailFormConfig, pwmRequest.getPwmSession().getLabel(), userInfo);
-            detailInfo.setSearchDetails(formData);
+            final List<DisplayElement> profileData = new ArrayList<>();
+            for (final Map.Entry<FormConfiguration, List<String>> entry : formData.entrySet()) {
+                final FormConfiguration formConfiguration = entry.getKey();
+                if (formConfiguration.isMultivalue()) {
+                    profileData.add(new DisplayElement(
+                            formConfiguration.getName(),
+                            DisplayElement.Type.multiString,
+                            formConfiguration.getLabel(actorLocale),
+                            entry.getValue()
+                    ));
+                } else {
+                    final String value = JavaHelper.isEmpty(entry.getValue())
+                            ? ""
+                            : entry.getValue().iterator().next();
+                    profileData.add(new DisplayElement(
+                            formConfiguration.getName(),
+                            DisplayElement.Type.string,
+                            formConfiguration.getLabel(actorLocale),
+                            value
+                    ));
+                }
+            }
+            detailInfo.profileData = profileData;
         }
 
         {
@@ -207,17 +220,26 @@ public class HelpdeskDetailInfoBean implements Serializable {
             detailInfo.setPasswordPolicyID(LocaleHelper.getLocalizedMessage(Display.Value_NotApplicable, pwmRequest));
         }
 
-        detailInfo.hasOtpRecord = userInfo.getOtpUserRecord() != null;
-
-        detailInfo.otpRecordTimestamp = userInfo.getOtpUserRecord() != null && userInfo.getOtpUserRecord().getTimestamp() != null
-                ? JavaHelper.toIsoDate(userInfo.getOtpUserRecord().getTimestamp())
-                : LocaleHelper.getLocalizedMessage(Display.Value_NotApplicable, pwmRequest);
-
-        detailInfo.responseInfoBean = userInfo.getResponseInfoBean();
+        {
+            final ResponseInfoBean responseInfoBean = userInfo.getResponseInfoBean();
+            if (responseInfoBean != null && responseInfoBean.getHelpdeskCrMap() != null) {
+                final List<DisplayElement> responseDisplay = new ArrayList<>();
+                int counter = 0;
+                for (final Map.Entry<Challenge, String> entry : responseInfoBean.getHelpdeskCrMap().entrySet()) {
+                    counter++;
+                    responseDisplay.add(new DisplayElement(
+                            "item_" + counter,
+                            DisplayElement.Type.string,
+                            entry.getKey().getChallengeText(),
+                            entry.getValue()
+                    ));
+                }
+                detailInfo.helpdeskResponses = responseDisplay;
+            }
 
-        detailInfo.setBackingUserInfo(userInfo);
+        }
 
-            final String configuredDisplayName = helpdeskProfile.readSettingAsString(PwmSetting.HELPDESK_DETAIL_DISPLAY_NAME);
+        final String configuredDisplayName = helpdeskProfile.readSettingAsString(PwmSetting.HELPDESK_DETAIL_DISPLAY_NAME);
         if (configuredDisplayName != null && !configuredDisplayName.isEmpty()) {
             final String displayName = macroMachine.expandMacros(configuredDisplayName);
             detailInfo.setUserDisplayName(displayName);
@@ -229,8 +251,120 @@ public class HelpdeskDetailInfoBean implements Serializable {
                     + " in " + timeDuration.asCompactString() + ", contents: " + JsonUtil.serialize(detailInfo));
         }
 
+        {
+            final Set<ViewStatusFields> viewStatusFields = helpdeskProfile.readSettingAsOptionList(PwmSetting.HELPDESK_VIEW_STATUS_VALUES, ViewStatusFields.class);
+            detailInfo.statusData = ViewableUserInfoDisplayReader.makeDisplayData(
+                    viewStatusFields,
+                    pwmRequest.getConfig(),
+                    userInfo,
+                    null,
+                    pwmRequest.getLocale()
+            );
+        }
+
+        detailInfo.setVisibleButtons(determineVisibleButtons(pwmRequest, helpdeskProfile));
+        detailInfo.setEnabledButtons(determineEnabledButtons(detailInfo.getVisibleButtons(), userInfo));
+        detailInfo.setCustomButtons(determineCustomButtons(helpdeskProfile));
 
         return detailInfo;
     }
+
+    static Set<StandardButton> determineVisibleButtons(
+            final PwmRequest pwmRequest,
+            final HelpdeskProfile helpdeskProfile
+    )
+    {
+        final Set<StandardButton> buttons = new LinkedHashSet<>();
+
+        buttons.add(StandardButton.refresh);
+        buttons.add(StandardButton.back);
+
+        {
+            final HelpdeskUIMode uiMode =
+                    helpdeskProfile.readSettingAsEnum(PwmSetting.HELPDESK_SET_PASSWORD_MODE, HelpdeskUIMode.class);
+            if (uiMode != HelpdeskUIMode.none) {
+                buttons.add(StandardButton.changePassword);
+            }
+        }
+
+        if (helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_ENABLE_UNLOCK)) {
+            buttons.add(StandardButton.unlock);
+        }
+
+        if (helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_CLEAR_RESPONSES_BUTTON)) {
+            buttons.add(StandardButton.clearResponses);
+        }
+
+        if (pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.OTP_ENABLED)) {
+            if (helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_CLEAR_OTP_BUTTON)) {
+                buttons.add(StandardButton.clearOtpSecret);
+            }
+        }
+
+        if (!helpdeskProfile.readOptionalVerificationMethods().isEmpty()) {
+            buttons.add(StandardButton.verification);
+        }
+
+        if (helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_DELETE_USER_BUTTON)) {
+            buttons.add(StandardButton.deleteUser);
+        }
+
+        return Collections.unmodifiableSet(buttons);
+    }
+
+    static Set<StandardButton> determineEnabledButtons(
+            final Set<StandardButton> visibleButtons,
+            final UserInfo userInfo
+    )
+            throws PwmUnrecoverableException
+    {
+        final Set<StandardButton> buttons = new LinkedHashSet<>(visibleButtons);
+
+        if (buttons.contains(StandardButton.unlock)) {
+            final boolean enabled = userInfo.isPasswordLocked();
+            if (!enabled) {
+                buttons.remove(StandardButton.unlock);
+            }
+        }
+
+        if (buttons.contains(StandardButton.clearResponses)) {
+            final boolean enabled = userInfo.getResponseInfoBean() != null;
+            if (!enabled) {
+                buttons.remove(StandardButton.clearResponses);
+            }
+        }
+
+        if (buttons.contains(StandardButton.clearOtpSecret)) {
+            final boolean enabled = userInfo.getOtpUserRecord() != null;
+            if (!enabled) {
+                buttons.remove(StandardButton.clearOtpSecret);
+            }
+        }
+
+        return Collections.unmodifiableSet(buttons);
+    }
+
+    static List<ButtonInfo> determineCustomButtons(
+            final HelpdeskProfile helpdeskProfile
+    )
+    {
+        final List<ActionConfiguration> actions = helpdeskProfile.readSettingAsAction(PwmSetting.HELPDESK_ACTIONS);
+
+        final List<ButtonInfo> buttons = new ArrayList<>();
+        if (actions != null) {
+            int count = 0;
+            for (final ActionConfiguration action : actions) {
+                buttons.add(new ButtonInfo(
+                        "custom_" + count++,
+                        action.getName(),
+                        action.getDescription()
+                ));
+            }
+        }
+
+        return Collections.unmodifiableList(buttons);
+
+    }
+
 }
 

+ 12 - 7
server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskServlet.java

@@ -537,14 +537,13 @@ public class HelpdeskServlet extends ControlledPwmServlet {
             intruderManager.convenience().clearUserIdentity(userIdentity);
         }
 
-        // send notice email
-        HelpdeskServletUtil.sendUnlockNoticeEmail(pwmRequest, helpdeskProfile, userIdentity);
 
-        final boolean useProxy = helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY);
         try {
-            final ChaiUser chaiUser = useProxy ?
-                    pwmRequest.getPwmApplication().getProxiedChaiUser(userIdentity) :
-                    pwmRequest.getPwmSession().getSessionManager().getActor(pwmRequest.getPwmApplication(), userIdentity);
+            final ChaiUser chaiUser = getChaiUser(pwmRequest, helpdeskProfile, userIdentity);
+
+            // send notice email
+            HelpdeskServletUtil.sendUnlockNoticeEmail(pwmRequest, helpdeskProfile, userIdentity, chaiUser);
+
             chaiUser.unlockPassword();
             {
                 // mark the event log
@@ -705,7 +704,13 @@ public class HelpdeskServlet extends ControlledPwmServlet {
             pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation, pwmRequest));
             return ProcessStatus.Halt;
         }
-        final UserInfo userInfo = helpdeskDetailInfoBean.getBackingUserInfo();
+        final UserInfo userInfo = UserInfoFactory.newUserInfo(
+                pwmRequest.getPwmApplication(),
+                pwmRequest.getSessionLabel(),
+                pwmRequest.getLocale(),
+                userIdentity,
+                getChaiUser(pwmRequest, helpdeskProfile, userIdentity).getChaiProvider()
+        );
         final MacroMachine macroMachine = new MacroMachine(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel(), userInfo, null);
         final String configuredTokenString = config.readAppProperty(AppProperty.HELPDESK_TOKEN_VALUE);
         final String tokenKey = macroMachine.expandMacros(configuredTokenString);

+ 20 - 9
server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskServletUtil.java

@@ -1,5 +1,6 @@
 package password.pwm.http.servlet.helpdesk;
 
+import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
@@ -18,6 +19,8 @@ import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.ldap.LdapPermissionTester;
+import password.pwm.ldap.UserInfo;
+import password.pwm.ldap.UserInfoFactory;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.HelpdeskAuditRecord;
@@ -95,10 +98,9 @@ class HelpdeskServletUtil {
             return;
         }
 
-        if (helpdeskDetailInfoBean != null && helpdeskDetailInfoBean.getUserInfo() != null) {
-            final String obfuscatedDN = helpdeskDetailInfoBean.getBackingUserInfo().getUserIdentity().toObfuscatedKey(pwmRequest.getPwmApplication());
+        if (helpdeskDetailInfoBean != null) {
+            final String obfuscatedDN = userIdentity.toObfuscatedKey(pwmRequest.getPwmApplication());
             pwmRequest.setAttribute(PwmRequestAttribute.HelpdeskObfuscatedDN, obfuscatedDN);
-            pwmRequest.setAttribute(PwmRequestAttribute.HelpdeskUsername, helpdeskDetailInfoBean.getUserInfo().getUserID());
         }
 
         pwmRequest.setAttribute(PwmRequestAttribute.HelpdeskDetail, helpdeskDetailInfoBean);
@@ -111,7 +113,7 @@ class HelpdeskServletUtil {
             final HelpdeskProfile helpdeskProfile,
             final UserIdentity userIdentity
     )
-            throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
+            throws ChaiUnavailableException, PwmUnrecoverableException
     {
         final UserIdentity actorUserIdentity = pwmRequest.getUserInfoIfLoggedIn().canonicalized(pwmRequest.getPwmApplication());
 
@@ -175,9 +177,11 @@ class HelpdeskServletUtil {
     static void sendUnlockNoticeEmail(
             final PwmRequest pwmRequest,
             final HelpdeskProfile helpdeskProfile,
-            final UserIdentity userIdentity
+            final UserIdentity userIdentity,
+            final ChaiUser chaiUser
+
     )
-            throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException {
+            throws PwmUnrecoverableException, ChaiUnavailableException {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final Configuration config = pwmRequest.getConfig();
         final Locale locale = pwmRequest.getLocale();
@@ -188,17 +192,24 @@ class HelpdeskServletUtil {
             return;
         }
 
-        final HelpdeskDetailInfoBean helpdeskDetailInfoBean = HelpdeskDetailInfoBean.makeHelpdeskDetailInfo(pwmRequest, helpdeskProfile, userIdentity);
+        final UserInfo userInfo = UserInfoFactory.newUserInfo(
+                pwmRequest.getPwmApplication(),
+                pwmRequest.getSessionLabel(),
+                pwmRequest.getLocale(),
+                userIdentity,
+                chaiUser.getChaiProvider()
+        );
+
         final MacroMachine macroMachine = new MacroMachine(
                 pwmApplication,
                 pwmRequest.getSessionLabel(),
-                helpdeskDetailInfoBean.getBackingUserInfo(),
+                userInfo,
                 null
         );
 
         pwmApplication.getEmailQueue().submitEmail(
                 configuredEmailSetting,
-                helpdeskDetailInfoBean.getBackingUserInfo(),
+                userInfo,
                 macroMachine
         );
     }

+ 6 - 0
server/src/main/java/password/pwm/ldap/UserInfo.java

@@ -66,6 +66,12 @@ public interface UserInfo {
 
     boolean isRequiresInteraction() throws PwmUnrecoverableException;
 
+    boolean isAccountEnabled() throws PwmUnrecoverableException;
+
+    boolean isAccountExpired() throws PwmUnrecoverableException;
+
+    boolean isPasswordLocked() throws PwmUnrecoverableException;
+
     Instant getPasswordLastModifiedTime() throws PwmUnrecoverableException;
 
     String getUserEmailAddress() throws PwmUnrecoverableException;

+ 4 - 0
server/src/main/java/password/pwm/ldap/UserInfoBean.java

@@ -80,6 +80,10 @@ public class UserInfoBean implements UserInfo {
     private final Instant lastLdapLoginTime;
     private final Instant accountExpirationTime;
 
+    private final boolean accountEnabled;
+    private final boolean accountExpired;
+    private final boolean passwordLocked;
+
     private final boolean requiresNewPassword;
     private final boolean requiresResponseConfig;
     private final boolean requiresOtpConfig;

+ 27 - 0
server/src/main/java/password/pwm/ldap/UserInfoReader.java

@@ -327,6 +327,33 @@ public class UserInfoReader implements UserInfo {
         return false;
     }
 
+    @Override
+    public boolean isAccountEnabled() throws PwmUnrecoverableException {
+        try {
+            return chaiUser.isAccountEnabled();
+        } catch (ChaiException e) {
+            throw PwmUnrecoverableException.fromChaiException(e);
+        }
+    }
+
+    @Override
+    public boolean isAccountExpired() throws PwmUnrecoverableException {
+        try {
+            return chaiUser.isAccountExpired();
+        } catch (ChaiException e) {
+            throw PwmUnrecoverableException.fromChaiException(e);
+        }
+    }
+
+    @Override
+    public boolean isPasswordLocked() throws PwmUnrecoverableException {
+        try {
+            return chaiUser.isPasswordLocked();
+        } catch (ChaiException e) {
+            throw PwmUnrecoverableException.fromChaiException(e);
+        }
+    }
+
     @Override
     public boolean isRequiresResponseConfig() throws PwmUnrecoverableException
     {

+ 313 - 0
server/src/main/java/password/pwm/ldap/ViewableUserInfoDisplayReader.java

@@ -0,0 +1,313 @@
+package password.pwm.ldap;
+
+import password.pwm.bean.LocalSessionStateBean;
+import password.pwm.bean.ResponseInfoBean;
+import password.pwm.config.Configuration;
+import password.pwm.config.PwmSetting;
+import password.pwm.config.option.ViewStatusFields;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.bean.DisplayElement;
+import password.pwm.i18n.Display;
+import password.pwm.util.LocaleHelper;
+import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.TimeDuration;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+public final class ViewableUserInfoDisplayReader {
+    private ViewableUserInfoDisplayReader() {
+    }
+
+    public static List<DisplayElement> makeDisplayData(
+            final Set<ViewStatusFields> viewStatusFields,
+            final Configuration config,
+            final UserInfo userInfo,
+            final LocalSessionStateBean localSessionStateBean,
+            final Locale locale
+    )
+            throws PwmUnrecoverableException {
+        final List<DisplayElement> accountInfo = new ArrayList<>();
+        final DataElementMaker maker = new DataElementMaker(viewStatusFields, config, locale, accountInfo);
+
+        maker.add(
+                ViewStatusFields.Username,
+                Display.Field_Username,
+                userInfo.getUsername()
+        );
+
+        maker.add(
+                ViewStatusFields.UserDN,
+                Display.Field_UserDN,
+                userInfo.getUserIdentity().getUserDN()
+        );
+
+        if (config.getLdapProfiles().size() > 1) {
+            final String ldapProfileID = userInfo.getUserIdentity().getLdapProfileID();
+            final String value = config.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.AccountEnabled,
+                Display.Field_AccountEnabled,
+                userInfo.isAccountEnabled()
+        );
+
+        maker.add(
+                ViewStatusFields.AccountExpired,
+                Display.Field_AccountExpired,
+                userInfo.isAccountExpired()
+        );
+
+        maker.add(
+                ViewStatusFields.GUID,
+                Display.Field_UserGUID,
+                userInfo.getUserGuid()
+        );
+
+        maker.add(
+                ViewStatusFields.AccountExpirationTime,
+                Display.Field_AccountExpirationTime,
+                userInfo.getAccountExpirationTime()
+        );
+
+        {
+            final Instant lastLoginTime = userInfo.getLastLdapLoginTime();
+            maker.add(
+                    ViewStatusFields.LastLoginTime,
+                    Display.Field_LastLoginTime,
+                    lastLoginTime
+            );
+
+            if (lastLoginTime != null) {
+                maker.add(
+                        ViewStatusFields.LastLoginTimeDelta,
+                        Display.Field_LastLoginTimeDelta,
+                        TimeDuration.fromCurrent(lastLoginTime).asLongString(locale)
+                );
+            }
+        }
+
+        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, config);
+            maker.add(
+                    ViewStatusFields.PasswordSetTimeDelta,
+                    Display.Field_PasswordSetTimeDelta,
+                    value
+            );
+        }
+
+        maker.add(
+                ViewStatusFields.PasswordExpireTime,
+                Display.Field_PasswordExpirationTime,
+                userInfo.getPasswordExpirationTime()
+        );
+
+        maker.add(
+                ViewStatusFields.IntruderDetect,
+                Display.Field_PasswordLocked,
+                userInfo.isPasswordLocked()
+        );
+
+        {
+            final ResponseInfoBean responseInfoBean = userInfo.getResponseInfoBean();
+            maker.add(
+                    ViewStatusFields.ResponsesStored,
+                    Display.Field_ResponsesStored,
+                    responseInfoBean != null
+            );
+
+            maker.add(
+                    ViewStatusFields.ResponsesNeeded,
+                    Display.Field_ResponsesNeeded,
+                    userInfo.isRequiresResponseConfig()
+            );
+
+
+            if (responseInfoBean != null) {
+                maker.add(
+                        ViewStatusFields.ResponsesTimestamp,
+                        Display.Field_ResponsesTimestamp,
+                        responseInfoBean.getTimestamp()
+                );
+            }
+        }
+
+        if (userInfo.getResponseInfoBean() != null) {
+            maker.add(
+                    ViewStatusFields.ResponsesTimestamp,
+                    Display.Field_ResponsesTimestamp,
+                    userInfo.getResponseInfoBean().getTimestamp()
+            );
+        }
+
+        if (config.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()
+                );
+            }
+        }
+
+        if (localSessionStateBean != null) {
+            maker.add(
+                    ViewStatusFields.NetworkAddress,
+                    Display.Field_NetworkAddress,
+                    localSessionStateBean.getSrcAddress()
+            );
+
+            maker.add(
+                    ViewStatusFields.NetworkHost,
+                    Display.Field_NetworkHost,
+                    localSessionStateBean.getSrcHostname()
+            );
+
+            {
+                final String value = localSessionStateBean.getLogoutURL() == null
+                        ? config.readSettingAsString(PwmSetting.URL_LOGOUT)
+                        : localSessionStateBean.getLogoutURL();
+
+                maker.add(
+                        ViewStatusFields.LogoutURL,
+                        Display.Field_LogoutURL,
+                        value
+                );
+            }
+
+            {
+                final String value = localSessionStateBean.getForwardURL() == null
+                        ? config.readSettingAsString(PwmSetting.URL_FORWARD)
+                        : localSessionStateBean.getForwardURL();
+
+                maker.add(
+                        ViewStatusFields.ForwardURL,
+                        Display.Field_ForwardURL,
+                        value
+                );
+            }
+        }
+
+
+        return Collections.unmodifiableList(accountInfo);
+    }
+
+    private static class DataElementMaker {
+        private final Configuration config;
+        private final Set<ViewStatusFields> viewStatusFields;
+        private final Locale locale;
+        private final List<DisplayElement> list;
+
+        DataElementMaker(final Set<ViewStatusFields> viewStatusFields, final Configuration config, final Locale locale, final List<DisplayElement> list) {
+            this.config = config;
+            this.viewStatusFields = viewStatusFields;
+            this.locale = locale;
+            this.list = list;
+        }
+
+        void add(final ViewStatusFields viewStatusField, final Display display, final Instant instant) {
+
+            if (!viewStatusFields.contains(viewStatusField)) {
+                return;
+            }
+
+            final String strValue = instant == null
+                    ? LocaleHelper.getLocalizedMessage(locale, Display.Value_NotApplicable, config)
+                    : JavaHelper.toIsoDate(instant);
+
+            list.add(new DisplayElement(
+                    display.name(),
+                    DisplayElement.Type.timestamp,
+                    LocaleHelper.getLocalizedMessage(locale, display, config),
+                    strValue
+            ));
+        }
+
+        void add(final ViewStatusFields viewStatusField, final Display display, final boolean value) {
+            add(viewStatusField, display, LocaleHelper.booleanString(
+                    value,
+                    locale,
+                    config
+            ));
+        }
+
+        void add(final ViewStatusFields viewStatusField, final Display display, final String value) {
+
+            if (!viewStatusFields.contains(viewStatusField)) {
+                return;
+            }
+
+            final String strValue = StringUtil.isEmpty(value)
+                    ? LocaleHelper.getLocalizedMessage(locale, Display.Value_NotApplicable, config)
+                    : value;
+
+            list.add(new DisplayElement(
+                    display.name(),
+                    DisplayElement.Type.string,
+                    LocaleHelper.getLocalizedMessage(locale, display, config),
+                    strValue
+            ));
+        }
+    }
+}

+ 53 - 374
server/src/main/webapp/WEB-INF/jsp/helpdesk-detail.jsp

@@ -20,49 +20,18 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   --%>
 
-<%@ page import="com.novell.ldapchai.ChaiPasswordRule" %>
-<%@ page import="com.novell.ldapchai.cr.Challenge" %>
-<%@ page import="password.pwm.bean.ResponseInfoBean" %>
-<%@ page import="password.pwm.bean.pub.PublicUserInfoBean" %>
-<%@ page import="password.pwm.config.PwmSetting" %>
-<%@ page import="password.pwm.config.option.HelpdeskUIMode" %>
-<%@ page import="password.pwm.config.option.ViewStatusFields" %>
-<%@ page import="password.pwm.config.profile.HelpdeskProfile" %>
-<%@ page import="password.pwm.config.profile.PwmPasswordRule" %>
-<%@ page import="password.pwm.config.value.data.ActionConfiguration" %>
-<%@ page import="password.pwm.config.value.data.FormConfiguration" %>
-<%@ page import="password.pwm.http.PwmSession" %>
 <%@ page import="password.pwm.http.servlet.helpdesk.HelpdeskDetailInfoBean" %>
-<%@ page import="password.pwm.http.tag.PasswordRequirementsTag" %>
-<%@ page import="password.pwm.i18n.Display" %>
-<%@ page import="password.pwm.svc.event.UserAuditRecord" %>
 <%@ page import="password.pwm.util.java.JavaHelper" %>
 <%@ page import="password.pwm.util.java.StringUtil" %>
-<%@ page import="password.pwm.util.java.TimeDuration" %>
-<%@ page import="password.pwm.util.macro.MacroMachine" %>
 <%@ page import="java.time.Instant" %>
-<%@ page import="java.util.Iterator" %>
-<%@ page import="java.util.List" %>
-<%@ page import="java.util.Set" %>
-<%@ page import="org.apache.commons.text.StringEscapeUtils" %>
+<%@ page import="password.pwm.http.bean.DisplayElement" %>
+<%@ page import="password.pwm.http.servlet.accountinfo.AccountInformationBean" %>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html; charset=UTF-8" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
-<%
-    final PwmRequest pwmRequest = JspUtility.getPwmRequest(pageContext);
-    final PwmSession pwmSession = pwmRequest.getPwmSession();
-    final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
-    final HelpdeskProfile helpdeskProfile = pwmSession.getSessionManager().getHelpdeskProfile(pwmApplication);
-    final HelpdeskUIMode SETTING_PW_UI_MODE = HelpdeskUIMode.valueOf(helpdeskProfile.readSettingAsString(PwmSetting.HELPDESK_SET_PASSWORD_MODE));
+<% final PwmRequest pwmRequest = JspUtility.getPwmRequest(pageContext); %>
+<% final HelpdeskDetailInfoBean helpdeskDetailInfoBean = (HelpdeskDetailInfoBean)pwmRequest.getAttribute(PwmRequestAttribute.HelpdeskDetail); %>
 
-    // user info
-    final HelpdeskDetailInfoBean helpdeskDetailInfoBean = (HelpdeskDetailInfoBean)pwmRequest.getAttribute(PwmRequestAttribute.HelpdeskDetail);
-    final PublicUserInfoBean searchedUserInfo = helpdeskDetailInfoBean.getUserInfo();
-    final ResponseInfoBean responseInfoBean = helpdeskDetailInfoBean.getResponseInfoBean();
-
-    final String displayName = helpdeskDetailInfoBean.getUserDisplayName();
-    final Set<ViewStatusFields> viewStatusFields = helpdeskProfile.readSettingAsOptionList(PwmSetting.HELPDESK_VIEW_STATUS_VALUES,ViewStatusFields.class);
-%>
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <%@ include file="/WEB-INF/jsp/fragment/header.jsp" %>
 <body class="nihilo">
@@ -72,14 +41,14 @@
     </jsp:include>
     <div id="centerbody" style="min-width: 800px">
         <div id="page-content-title"><pwm:display key="Title_Helpdesk" displayIfMissing="true"/></div>
-        <% if (displayName != null && !displayName.isEmpty()) { %>
-        <h2 style="text-align: center"><%=displayName%></h2>
+        <% if (!StringUtil.isEmpty(helpdeskDetailInfoBean.getUserDisplayName())) { %>
+        <h2 style="text-align: center"><%=StringUtil.escapeHtml(helpdeskDetailInfoBean.getUserDisplayName())%></h2>
         <% } %>
         <pwm:script>
             <script type="text/javascript">
                 PWM_GLOBAL['startupFunctions'].push(function(){
                     PWM_VAR["helpdesk_obfuscatedDN"] = '<%=JspUtility.getAttribute(pageContext, PwmRequestAttribute.HelpdeskObfuscatedDN)%>';
-                    PWM_VAR["helpdesk_username"] = '<%=StringUtil.escapeJS((String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.HelpdeskUsername))%>';
+                    PWM_VAR["helpdesk_username"] = '<%=helpdeskDetailInfoBean.getUserDisplayName()%>';
                 });
             </script>
         </pwm:script>
@@ -91,313 +60,18 @@
                         <div id="Field_Profile" data-dojo-type="dijit.layout.ContentPane" title="<pwm:display key="Field_Profile"/>" class="tabContent">
                             <div style="max-height: 400px; overflow: auto;">
                                 <table class="nomargin">
-                                    <% for (final FormConfiguration formItem : helpdeskDetailInfoBean.getSearchDetails().keySet()) { %>
-                                    <tr>
-                                        <td class="key" id="key_<%=StringUtil.escapeHtml(formItem.getName())%>" title="<%=StringUtil.escapeHtml(formItem.getDescription(pwmRequest.getLocale()))%>">
-                                            <%= formItem.getLabel(pwmSession.getSessionStateBean().getLocale())%>
-                                        </td>
-                                        <td id="value_<%=formItem.getName()%>">
-                                            <% for (final Iterator<String> iter = helpdeskDetailInfoBean.getSearchDetails().get(formItem).iterator(); iter.hasNext(); ) { %>
-                                            <% final String loopValue = iter.next(); %>
-                                            <%= loopValue == null ? "" : StringUtil.escapeHtml(loopValue) %>
-                                            <% if (iter.hasNext()) { %> <br/> <% } %>
-                                            <% } %>
-                                        </td>
-                                    </tr>
-                                    <%  } %>
+                                    <% for (final DisplayElement displayElement : helpdeskDetailInfoBean.getProfileData()) { %>
+                                    <% request.setAttribute("displayElement", displayElement); %>
+                                    <jsp:include page="fragment/displayelement-row.jsp"/>
+                                    <% } %>
                                 </table>
                             </div>
                         </div>
                         <div id="Title_Status" data-dojo-type="dijit.layout.ContentPane" title="<pwm:display key="Title_Status"/>" class="tabContent">
                             <table class="nomargin">
-                                <% if (viewStatusFields.contains(ViewStatusFields.UserDN)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_UserDN"/>
-                                    </td>
-                                    <td>
-                                        <span style="word-wrap: break-word; word-break: break-all">
-                                        <%= StringUtil.escapeHtml(searchedUserInfo.getUserDN()) %>
-                                        </span>
-                                    </td>
-                                </tr>
-                                <% if (pwmApplication.getConfig().getLdapProfiles().size() > 1) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_LdapProfile"/>
-                                    </td>
-                                    <td>
-                                        <%= StringUtil.escapeHtml(pwmApplication.getConfig().getLdapProfiles().get(searchedUserInfo.getLdapProfile()).getDisplayName(pwmSession.getSessionStateBean().getLocale())) %>
-                                    </td>
-                                </tr>
-                                <% } %>
-                                <% } %>
-                                <% if (viewStatusFields.contains(ViewStatusFields.Username)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_Username"/>
-                                    </td>
-                                    <td>
-                                        <%= StringUtil.escapeHtml(searchedUserInfo.getUserID()) %>
-                                    </td>
-                                </tr>
-                                <% } %>
-                                <% if (viewStatusFields.contains(ViewStatusFields.UserEmail)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_UserEmail"/>
-                                    </td>
-                                    <td>
-                                        <% if (searchedUserInfo.getUserEmailAddress() == null) { %>
-                                        <pwm:display key="Value_NotApplicable"/>
-                                        <% } else { %>
-                                        <%= StringUtil.escapeHtml(searchedUserInfo.getUserEmailAddress()) %>
-                                        <% } %>
-                                    </td>
-                                </tr>
-                                <% } %>
-                                <% if (viewStatusFields.contains(ViewStatusFields.UserSMS)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_UserSMS"/>
-                                    </td>
-                                    <td>
-                                        <% if (searchedUserInfo.getUserSmsNumber() == null) { %>
-                                        <pwm:display key="Value_NotApplicable"/>
-                                        <% } else { %>
-                                        <%= StringUtil.escapeHtml(searchedUserInfo.getUserSmsNumber()) %>
-                                        <% } %>
-                                    </td>
-                                </tr>
-                                <% } %>
-                                <% if (viewStatusFields.contains(ViewStatusFields.AccountEnabled)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_AccountEnabled"/>
-                                    </td>
-                                    <td>
-                                        <%if (helpdeskDetailInfoBean.isAccountEnabled()) { %><pwm:display key="Value_True"/><% } else { %><pwm:display key="Value_False"/><% } %>
-                                    </td>
-                                </tr>
-                                <% } %>
-                                <% if (viewStatusFields.contains(ViewStatusFields.AccountExpired)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_AccountExpired"/>
-                                    </td>
-                                    <td>
-                                        <%if (helpdeskDetailInfoBean.isAccountExpired()) { %><pwm:display key="Value_True"/><% } else { %><pwm:display key="Value_False"/><% } %>
-                                    </td>
-                                </tr>
-                                <% } %>
-                                <% if (viewStatusFields.contains(ViewStatusFields.AccountExpirationTime)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_AccountExpirationTime"/>
-                                    </td>
-                                    <% if (searchedUserInfo.getAccountExpirationTime() == null) { %>
-                                    <td>
-                                        <pwm:display key="Value_NotApplicable"/>
-                                    </td>
-                                    <% } else { %>
-                                    <td class="timestamp">
-                                        <%= JavaHelper.toIsoDate(searchedUserInfo.getAccountExpirationTime()) %>
-                                    </td>
-                                    <% } %>
-                                    </td>
-                                </tr>
-                                <% } %>
-                                <% if (viewStatusFields.contains(ViewStatusFields.LastLoginTime)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_LastLoginTime"/>
-                                    </td>
-                                    <% if (helpdeskDetailInfoBean.getLastLoginTime() == null) { %>
-                                    <td>
-                                        <pwm:display key="Value_NotApplicable"/>
-                                    </td>
-                                    <% } else { %>
-                                    <td class="timestamp">
-                                        <%= JavaHelper.toIsoDate(helpdeskDetailInfoBean.getLastLoginTime()) %>
-                                    </td>
-                                    <% } %>
-                                </tr>
-                                <% } %>
-                                <% if (viewStatusFields.contains(ViewStatusFields.LastLoginTimeDelta)) { %>
-                                <% if (helpdeskDetailInfoBean.getLastLoginTime() != null) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_LastLoginTimeDelta"/>
-                                    </td>
-                                    <td>
-                                        <%= TimeDuration.fromCurrent(helpdeskDetailInfoBean.getLastLoginTime()).asLongString(pwmSession.getSessionStateBean().getLocale()) %>
-                                    </td>
-                                </tr>
-                                <% } %>
-                                <% } %>
-                                <% if (viewStatusFields.contains(ViewStatusFields.PasswordExpired)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_PasswordExpired"/>
-                                    </td>
-                                    <td>
-                                        <%if (searchedUserInfo.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>
-                                        <%if (searchedUserInfo.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>
-                                        <%if (searchedUserInfo.getPasswordStatus().isWarnPeriod()) { %><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 (searchedUserInfo.getPasswordLastModifiedTime() == null) { %>
-                                    <td>
-                                        <pwm:display key="Value_NotApplicable"/>
-                                    </td>
-                                    <% } else { %>
-                                    <td class="timestamp">
-                                        <%= JavaHelper.toIsoDate(searchedUserInfo.getPasswordLastModifiedTime()) %>
-                                    </td>
-                                    <% } %>
-                                </tr>
-                                <% } %>
-                                <% if (viewStatusFields.contains(ViewStatusFields.PasswordSetTimeDelta)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_PasswordSetTimeDelta"/>
-                                    </td>
-                                    <td>
-                                        <%= helpdeskDetailInfoBean.getPasswordSetDelta() %>
-                                    </td>
-                                </tr>
-                                <% } %>
-                                <% if (viewStatusFields.contains(ViewStatusFields.PasswordExpireTime)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_PasswordExpirationTime"/>
-                                    </td>
-                                    <% if (searchedUserInfo.getPasswordExpirationTime() == null) { %>
-                                    <td>
-                                        <pwm:display key="Value_NotApplicable"/>
-                                    </td>
-                                    <% } else { %>
-                                    <td class="timestamp">
-                                        <%= JavaHelper.toIsoDate(searchedUserInfo.getPasswordExpirationTime()) %>
-                                    </td>
-                                    <% } %>
-                                </tr>
-                                <% } %>
-                                <% if (viewStatusFields.contains(ViewStatusFields.IntruderDetect)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_PasswordLocked"/>
-                                    </td>
-                                    <% if (helpdeskDetailInfoBean.isIntruderLocked()) { %>
-                                    <td class="health-WARN">
-                                        <pwm:display key="Value_True"/>
-                                    </td>
-                                    <% } else { %>
-                                    <td>
-                                        <pwm:display key="Value_False"/>
-                                    </td>
-                                    <% } %>
-                                </tr>
-                                <% } %>
-                                <% if (viewStatusFields.contains(ViewStatusFields.ResponsesStored)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_ResponsesStored"/>
-                                    </td>
-                                    <td>
-                                        <% if (responseInfoBean != null) { %>
-                                        <pwm:display key="Value_True"/>
-                                        <% } else { %>
-                                        <pwm:display key="Value_False"/>
-                                        <% } %>
-                                    </td>
-                                </tr>
-                                <% } %>
-                                <% if (viewStatusFields.contains(ViewStatusFields.ResponsesNeeded)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_ResponsesNeeded"/>
-                                    </td>
-                                    <td>
-                                        <% if (searchedUserInfo.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>
-                                        <pwm:display key="Value_NotApplicable"/>
-                                    </td>
-                                    <% } else { %>
-                                    <td class="timestamp">
-                                        <%= 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>
-                                            <%if (helpdeskDetailInfoBean.isHasOtpRecord()) {%><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>
-                                        <td class="timestamp">
-                                            <%= helpdeskDetailInfoBean.getOtpRecordTimestamp() %>
-                                        </td>
-                                    </tr>
-                                    <% } %>
-                                </pwm:if>
-                                <% if (viewStatusFields.contains(ViewStatusFields.GUID)) { %>
-                                <tr>
-                                    <td class="key">
-                                        <pwm:display key="Field_UserGUID"/>
-                                    </td>
-                                    <td>
-                                        <%= StringUtil.escapeHtml(searchedUserInfo.getUserGUID()) %>
-                                    </td>
-                                </tr>
+                                <% for (final DisplayElement displayElement : helpdeskDetailInfoBean.getStatusData()) { %>
+                                <% request.setAttribute("displayElement", displayElement); %>
+                                <jsp:include page="fragment/displayelement-row.jsp"/>
                                 <% } %>
                             </table>
                         </div>
@@ -405,14 +79,13 @@
                         <div id="Title_UserEventHistory" data-dojo-type="dijit.layout.ContentPane" title="<pwm:display key="Title_UserEventHistory"/>" class="tabContent">
                             <div style="max-height: 400px; overflow: auto;">
                                 <table class="nomargin">
-                                    <% for (final UserAuditRecord record : helpdeskDetailInfoBean.getUserHistory()) { %>
+                                    <% for (final AccountInformationBean.ActivityRecord record : helpdeskDetailInfoBean.getUserHistory()) { %>
                                     <tr>
                                         <td class="key timestamp" style="width:50%">
                                             <%= JavaHelper.toIsoDate(record.getTimestamp()) %>
                                         </td>
                                         <td>
-                                            <%= record.getEventCode().getLocalizedString(pwmRequest.getConfig(), pwmRequest.getLocale()) %>
-                                            <%= record.getMessage() != null && record.getMessage().length() > 1 ? " (" + record.getMessage() + ") " : "" %>
+                                            <%= record.getLabel() %>
                                         </td>
                                     </tr>
                                     <% } %>
@@ -467,18 +140,12 @@
                                 </table>
                             </div>
                         </div>
-                        <% if (responseInfoBean != null && responseInfoBean.getHelpdeskCrMap() != null && !responseInfoBean.getHelpdeskCrMap().isEmpty()) { %>
+                        <% if (!JavaHelper.isEmpty(helpdeskDetailInfoBean.getHelpdeskResponses())) { %>
                         <div id="Title_SecurityResponses" data-dojo-type="dijit.layout.ContentPane" title="<pwm:display key="Title_SecurityResponses"/>" class="tabContent">
                             <table class="nomargin">
-                                <% for (final Challenge challenge : responseInfoBean.getHelpdeskCrMap().keySet()) { %>
-                                <tr>
-                                    <td class="key">
-                                        <%=challenge.getChallengeText()%>
-                                    </td>
-                                    <td>
-                                        <%=responseInfoBean.getHelpdeskCrMap().get(challenge)%>
-                                    </td>
-                                </tr>
+                                <% for (final DisplayElement displayElement : helpdeskDetailInfoBean.getHelpdeskResponses()) { %>
+                                <% request.setAttribute("displayElement", displayElement); %>
+                                <jsp:include page="fragment/displayelement-row.jsp"/>
                                 <% } %>
                             </table>
                         </div>
@@ -489,23 +156,31 @@
                 </td>
                 <td class="noborder" style="width: 200px; max-width:200px; text-align: left; vertical-align: top">
                     <div class="noborder" style="margin-top: 25px; margin-left: 5px">
-                        <button name="button_continue" class="helpdesk-detail-btn btn" id="button_continue" autofocus>
+                        <% if (helpdeskDetailInfoBean.getVisibleButtons().contains(HelpdeskDetailInfoBean.StandardButton.back)) { %>
+                        <button name="they" class="helpdesk-detail-btn btn" id="button_continue" autofocus>
                             <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-backward"></span></pwm:if>
                             <pwm:display key="Button_GoBack"/>
                         </button>
+                        <% } %>
+
+                        <% if (helpdeskDetailInfoBean.getVisibleButtons().contains(HelpdeskDetailInfoBean.StandardButton.refresh)) { %>
                         <button name="button_refresh" class="helpdesk-detail-btn btn" id="button_refresh">
                             <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-refresh"></span></pwm:if>
                             <pwm:display key="Display_CaptchaRefresh"/>
                         </button>
+                        <% } %>
+
                         <br/><br/>
-                        <% if (SETTING_PW_UI_MODE != HelpdeskUIMode.none) { %>
+
+                        <% if (helpdeskDetailInfoBean.getVisibleButtons().contains(HelpdeskDetailInfoBean.StandardButton.changePassword)) { %>
                         <button class="helpdesk-detail-btn btn" id="helpdesk_ChangePasswordButton">
                             <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-key"></span></pwm:if>
                             <pwm:display key="Button_ChangePassword"/>
                         </button>
                         <% } %>
-                        <% if (helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_ENABLE_UNLOCK)) { %>
-                        <% if (helpdeskDetailInfoBean.isIntruderLocked()) { %>
+
+                        <% if (helpdeskDetailInfoBean.getVisibleButtons().contains(HelpdeskDetailInfoBean.StandardButton.unlock)) { %>
+                        <% if (helpdeskDetailInfoBean.getEnabledButtons().contains(HelpdeskDetailInfoBean.StandardButton.unlock)) { %>
                         <button id="helpdesk_unlockBtn" class="helpdesk-detail-btn btn">
                             <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-unlock"></span></pwm:if>
                             <pwm:display key="Button_Unlock"/>
@@ -517,8 +192,9 @@
                         </button>
                         <% } %>
                         <% } %>
-                        <% if (helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_CLEAR_RESPONSES_BUTTON)) { %>
-                        <% if (responseInfoBean != null) { %>
+
+                        <% if (helpdeskDetailInfoBean.getVisibleButtons().contains(HelpdeskDetailInfoBean.StandardButton.clearResponses)) { %>
+                        <% if (helpdeskDetailInfoBean.getEnabledButtons().contains(HelpdeskDetailInfoBean.StandardButton.clearResponses)) { %>
                         <button id="helpdesk_clearResponsesBtn" class="helpdesk-detail-btn btn">
                             <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-eraser"></span></pwm:if>
                             <pwm:display key="Button_ClearResponses"/>
@@ -535,13 +211,13 @@
                                         id: "helpdesk_clearResponsesBtn",
                                         text: 'User does not have responses'
                                     });
-                                });
-                            </script>
+                                });</script>
                         </pwm:script>
                         <% } %>
                         <% } %>
-                        <% if (helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_CLEAR_OTP_BUTTON) && pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.OTP_ENABLED)) { %>
-                        <% if (helpdeskDetailInfoBean.isHasOtpRecord()) { %>
+
+                        <% if (helpdeskDetailInfoBean.getVisibleButtons().contains(HelpdeskDetailInfoBean.StandardButton.clearOtpSecret)) { %>
+                        <% if (helpdeskDetailInfoBean.getEnabledButtons().contains(HelpdeskDetailInfoBean.StandardButton.clearOtpSecret)) { %>
                         <button id="helpdesk_clearOtpSecretBtn" class="helpdesk-detail-btn btn">
                             <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-eraser"></span></pwm:if>
                             <pwm:display key="Button_HelpdeskClearOtpSecret"/>
@@ -553,19 +229,20 @@
                         </button>
                         <% } %>
                         <% } %>
-                        <% if ((Boolean)JspUtility.getPwmRequest(pageContext).getAttribute(PwmRequestAttribute.HelpdeskVerificationEnabled) == true) { %>
+
+                        <% if (helpdeskDetailInfoBean.getVisibleButtons().contains(HelpdeskDetailInfoBean.StandardButton.verification)) { %>
                         <button id="sendTokenButton" class="helpdesk-detail-btn btn">
                             <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-mobile-phone"></span></pwm:if>
                             <pwm:display key="Button_Verify"/>
                         </button>
                         <% } %>
-                        <% if (helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_DELETE_USER_BUTTON)) { %>
+
+                        <% if (helpdeskDetailInfoBean.getVisibleButtons().contains(HelpdeskDetailInfoBean.StandardButton.deleteUser)) { %>
                         <button class="helpdesk-detail-btn btn" id="helpdesk_deleteUserButton">
                             <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-user-times"></span></pwm:if>
                             <pwm:display key="Button_Delete"/>
                         </button>
                         <% } %>
-                        <% final List<ActionConfiguration> actions = helpdeskProfile.readSettingAsAction(PwmSetting.HELPDESK_ACTIONS); %>
 
                         <button id="loadDetail" style="display:none">Load Detail</button>
                         <pwm:script>
@@ -575,7 +252,7 @@
                                         var url = 'helpdesk';
                                         url = PWM_MAIN.addParamToUrl(url, 'processAction', 'detail');
                                         url = PWM_MAIN.addParamToUrl(url, 'userKey', PWM_VAR['helpdesk_obfuscatedDN']);
-                                        url = PWM_MAIN.addParamToUrl(url, 'verificationState', PWM_MAIN.Preferences.readSessionStorage(PREF_KEY_VERIFICATION_STATE));
+                                        //url = PWM_MAIN.addParamToUrl(url, 'verificationState', PWM_MAIN.Preferences.readSessionStorage(PREF_KEY_VERIFICATION_STATE));
                                         PWM_MAIN.ajaxRequest(url,function () {
                                         });
                                     });
@@ -583,26 +260,28 @@
                             </script>
                         </pwm:script>
 
-                        <% for (final ActionConfiguration loopAction : actions) { %>
-                        <button class="helpdesk-detail-btn btn" name="action-<%=loopAction.getName()%>" id="action-<%=loopAction.getName()%>">
+                        <% if (!JavaHelper.isEmpty(helpdeskDetailInfoBean.getCustomButtons())) { %>
+                        <% for (final HelpdeskDetailInfoBean.ButtonInfo customButton : helpdeskDetailInfoBean.getCustomButtons()) { %>
+                        <button class="helpdesk-detail-btn btn" name="action-<%=customButton.getName()%>" id="action-<%=customButton.getName()%>">
                             <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-location-arrow"></span></pwm:if>
-                            <%=StringUtil.escapeHtml(loopAction.getName())%>
+                            <%=StringUtil.escapeHtml(customButton.getLabel())%>
                         </button>
                         <pwm:script>
                             <script type="text/javascript">
                                 PWM_GLOBAL['startupFunctions'].push(function(){
-                                    PWM_MAIN.addEventHandler('action-<%=loopAction.getName()%>','click',function(){
-                                        PWM_HELPDESK.executeAction('<%=StringUtil.escapeJS(loopAction.getName())%>');
+                                    PWM_MAIN.addEventHandler('action-<%=customButton.getName()%>','click',function(){
+                                        PWM_HELPDESK.executeAction('<%=StringUtil.escapeJS(customButton.getName())%>');
                                     });
                                     PWM_MAIN.showTooltip({
-                                        id: "action-<%=loopAction.getName()%>",
+                                        id: "action-<%=customButton.getName()%>",
                                         position: 'above',
-                                        text: '<%=StringUtil.escapeJS(loopAction.getDescription())%>'
+                                        text: '<%=StringUtil.escapeJS(customButton.getDescription())%>'
                                     });
                                 });
                             </script>
                         </pwm:script>
                         <% } %>
+                        <% } %>
                     </div>
                 </td>
             </tr>