瀏覽代碼

convert rest AppData server to client api servlet

Jason Rivard 7 年之前
父節點
當前提交
eab866ac65

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

@@ -45,6 +45,8 @@ public enum HttpHeader {
     Referer("Referer"),
     Origin("Origin"),
     XForwardedFor("X-Forwarded-For"),
+    ETag("ETag"),
+    Expires("Expires"),
 
     XFrameOptions("X-Frame-Options"),
     XContentTypeOptions("X-Content-Type-Options"),

+ 4 - 0
server/src/main/java/password/pwm/http/PwmURL.java

@@ -172,6 +172,10 @@ public class PwmURL {
         return checkIfStartsWithURL(PwmConstants.URL_PREFIX_PRIVATE + "/config/");
     }
 
+    public boolean isClientApiServlet() {
+        return isPwmServletURL(PwmServletDefinition.ClientApi);
+    }
+
     public boolean isConfigGuideURL() {
         return isPwmServletURL(PwmServletDefinition.ConfigGuide);
     }

+ 1 - 1
server/src/main/java/password/pwm/http/filter/ApplicationModeFilter.java

@@ -52,7 +52,7 @@ public class ApplicationModeFilter extends AbstractPwmFilter {
     {
         // ignore if resource request
         final PwmURL pwmURL = pwmRequest.getURL();
-        if (!pwmURL.isResourceURL() && !pwmURL.isStandaloneWebService() && !pwmURL.isReferenceURL()) {
+        if (!pwmURL.isResourceURL() && !pwmURL.isStandaloneWebService() && !pwmURL.isReferenceURL() && !pwmURL.isClientApiServlet()) {
             // check for valid config
             try {
                 if (checkConfigModes(pwmRequest) == ProcessStatus.Halt) {

+ 188 - 264
server/src/main/java/password/pwm/ws/server/rest/RestAppDataServer.java → server/src/main/java/password/pwm/http/servlet/ClientApiServlet.java

@@ -1,53 +1,30 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package password.pwm.ws.server.rest;
+package password.pwm.http.servlet;
 
 import com.novell.ldapchai.exception.ChaiUnavailableException;
+import lombok.Data;
 import password.pwm.AppProperty;
 import password.pwm.Permission;
 import password.pwm.PwmApplication;
+import password.pwm.PwmApplicationMode;
 import password.pwm.PwmConstants;
-import password.pwm.bean.pub.SessionStateInfoBean;
-import password.pwm.config.value.data.ActionConfiguration;
 import password.pwm.config.Configuration;
-import password.pwm.config.value.data.FormConfiguration;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.option.SelectableContextMode;
+import password.pwm.config.value.data.ActionConfiguration;
+import password.pwm.config.value.data.FormConfiguration;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpHeader;
+import password.pwm.http.HttpMethod;
 import password.pwm.http.IdleTimeoutCalculator;
+import password.pwm.http.ProcessStatus;
+import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmURL;
-import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.i18n.Display;
-import password.pwm.svc.event.AuditRecord;
-import password.pwm.svc.event.HelpdeskAuditRecord;
-import password.pwm.svc.event.SystemAuditRecord;
-import password.pwm.svc.event.UserAuditRecord;
-import password.pwm.svc.intruder.RecordType;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.java.TimeDuration;
@@ -55,29 +32,21 @@ import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroMachine;
 import password.pwm.util.secure.PwmHashAlgorithm;
 import password.pwm.util.secure.SecureEngine;
-import password.pwm.ws.server.RestRequestBean;
 import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.RestServerHelper;
-import password.pwm.ws.server.ServicePermissions;
+import password.pwm.ws.server.rest.RestHealthServer;
+import password.pwm.ws.server.rest.bean.HealthData;
 
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
 import java.io.IOException;
 import java.io.Serializable;
 import java.net.URI;
-import java.net.URISyntaxException;
+import java.time.Instant;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
@@ -86,220 +55,205 @@ import java.util.ResourceBundle;
 import java.util.TreeMap;
 import java.util.TreeSet;
 
-@Path("/app-data")
-public class RestAppDataServer extends AbstractRestServer {
-
-    private static final PwmLogger LOGGER = PwmLogger.forClass(RestAppDataServer.class);
+@WebServlet(
+        name="ClientApiServlet",
+        urlPatterns = {
+                PwmConstants.URL_PREFIX_PUBLIC + "/api",
+        }
+)
+public class ClientApiServlet extends ControlledPwmServlet {
 
-    private static final ServicePermissions SERVICE_PERMISSIONS = ServicePermissions.builder()
-            .adminOnly(true)
-            .authRequired(true)
-            .blockExternal(true)
-            .build();
+    private static final PwmLogger LOGGER = PwmLogger.forClass(ClientApiServlet.class);
 
+    @Data
     public static class AppData implements Serializable {
         public Map<String,Object> PWM_GLOBAL;
     }
 
-
-
-    @GET
-    @Produces(MediaType.TEXT_HTML)
-    public javax.ws.rs.core.Response doHtmlRedirect() throws URISyntaxException {
-        return RestServerHelper.handleHtmlRequest();
+    @Data
+    public static class PingResponse implements Serializable {
+        private Instant time;
+        private String runtimeNonce;
     }
 
-    @GET
-    @Path("/audit")
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    public Response doGetAppAuditData(
-            @QueryParam("maximum") final int maximum
-    )
-            throws ChaiUnavailableException, PwmUnrecoverableException
-    {
-        final int max = maximum > 0
-                ? maximum
-                : 10 * 1000;
-
-        final RestRequestBean restRequestBean;
-        try {
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, null);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
+    public enum ClientApiAction implements AbstractPwmServlet.ProcessAction {
+        clientData(HttpMethod.GET),
+        strings(HttpMethod.GET),
+        health(HttpMethod.GET),
+        ping(HttpMethod.GET),
 
-        final ArrayList<UserAuditRecord> userRecords = new ArrayList<>();
-        final ArrayList<HelpdeskAuditRecord> helpdeskRecords = new ArrayList<>();
-        final ArrayList<SystemAuditRecord> systemRecords = new ArrayList<>();
-        final Iterator<AuditRecord> iterator = restRequestBean.getPwmApplication().getAuditManager().readVault();
-        int counter = 0;
-        while (iterator.hasNext() && counter <= max) {
-            final AuditRecord loopRecord = iterator.next();
-            counter++;
-            if (loopRecord instanceof SystemAuditRecord) {
-                systemRecords.add((SystemAuditRecord)loopRecord);
-            } else if (loopRecord instanceof HelpdeskAuditRecord) {
-                helpdeskRecords.add((HelpdeskAuditRecord)loopRecord);
-            } else if (loopRecord instanceof UserAuditRecord) {
-                userRecords.add((UserAuditRecord)loopRecord);
-            }
-        }
-        final HashMap<String,List> outputMap = new HashMap<>();
-        outputMap.put("user",userRecords);
-        outputMap.put("helpdesk",helpdeskRecords);
-        outputMap.put("system",systemRecords);
+        ;
 
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(outputMap);
-        LOGGER.debug(restRequestBean.getPwmSession(),"output " + counter + " audit records.");
-        return restResultBean.asJsonResponse();
-    }
+        private final HttpMethod method;
 
-    @GET
-    @Path("/session")
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    public Response doGetAppSessionData(
-            @QueryParam("maximum") final int maximum
-    )
-            throws ChaiUnavailableException, PwmUnrecoverableException
-    {
-        final int max = maximum > 0
-                ? maximum
-                : 10 * 1000;
-        final RestRequestBean restRequestBean;
-        try {
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, null);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
-
-        if (!restRequestBean.getPwmSession().getSessionManager().checkPermission(restRequestBean.getPwmApplication(), Permission.PWMADMIN)) {
-            final ErrorInformation errorInfo = PwmError.ERROR_UNAUTHORIZED.toInfo();
-            return RestResultBean.fromError(errorInfo, restRequestBean).asJsonResponse();
+        ClientApiAction(final HttpMethod method)
+        {
+            this.method = method;
         }
 
-        final ArrayList<SessionStateInfoBean> gridData = new ArrayList<>();
-        int counter = 0;
-        final Iterator<SessionStateInfoBean> infos = restRequestBean.getPwmApplication().getSessionTrackService().getSessionInfoIterator();
-        while (counter < max && infos.hasNext()) {
-            gridData.add(infos.next());
-            counter++;
+        public Collection<HttpMethod> permittedMethods()
+        {
+            return Collections.singletonList(method);
         }
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(gridData);
-        return restResultBean.asJsonResponse();
     }
 
-    @GET
-    @Path("/intruder")
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    public Response doGetAppIntruderData(
-            @QueryParam("maximum") final int maximum
-    )
-            throws ChaiUnavailableException, PwmUnrecoverableException
-    {
-        final int max = maximum > 0
-                ? maximum
-                : 10 * 1000;
-
-        final RestRequestBean restRequestBean;
-        try {
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, null);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
-
-        if (!restRequestBean.getPwmSession().getSessionManager().checkPermission(restRequestBean.getPwmApplication(), Permission.PWMADMIN)) {
-            final ErrorInformation errorInfo = PwmError.ERROR_UNAUTHORIZED.toInfo();
-            return RestResultBean.fromError(errorInfo, restRequestBean).asJsonResponse();
-        }
-
-        final TreeMap<String,Object> returnData = new TreeMap<>();
-        try {
-            for (final RecordType recordType : RecordType.values()) {
-                returnData.put(recordType.toString(),restRequestBean.getPwmApplication().getIntruderManager().getRecords(recordType, max));
-            }
-        } catch (PwmException e) {
-            final ErrorInformation errorInfo = new ErrorInformation(PwmError.ERROR_UNKNOWN,e.getMessage());
-            return RestResultBean.fromError(errorInfo, restRequestBean).asJsonResponse();
-        }
+    @Override
+    public Class<? extends ProcessAction> getProcessActionsClass() {
+        return ClientApiServlet.ClientApiAction.class;
+    }
 
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(returnData);
-        return restResultBean.asJsonResponse();
+    @Override
+    protected void nextStep(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException {
+        // no mvc pattern in this servlet
+    }
 
+    @Override
+    public ProcessStatus preProcessCheck(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ServletException {
+        return ProcessStatus.Continue;
     }
 
-    @GET
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    @Path("/client")
-    public Response doGetAppClientData(
-            @QueryParam("pageUrl") final String pageUrl,
-            @PathParam(value = "eTagUri") final String eTagUri,
-            @Context final HttpServletRequest request,
-            @Context final HttpServletResponse response
-    )
+
+    @ActionHandler(action = "clientData")
+    public ProcessStatus processRestClientData(final PwmRequest pwmRequest)
             throws PwmUnrecoverableException, IOException, ChaiUnavailableException
     {
+        final String pageUrl = pwmRequest.readParameterAsString("pageUrl", PwmHttpRequestWrapper.Flag.BypassValidation);
+        final String etagParam = pwmRequest.readParameterAsString("etag", PwmHttpRequestWrapper.Flag.BypassValidation);
+
         final int maxCacheAgeSeconds = 60 * 5;
-        final RestRequestBean restRequestBean;
-        try {
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, ServicePermissions.PUBLIC, null);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
 
-        final String eTagValue = makeClientEtag(restRequestBean.getPwmApplication(), restRequestBean.getPwmSession(), request);
+        final String eTagValue = makeClientEtag(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), pwmRequest.getHttpServletRequest());
 
         // check the incoming header;
-        final String ifNoneMatchValue = request.getHeader("If-None-Match");
+        final String ifNoneMatchValue = pwmRequest.readHeaderValueAsString("If-None-Match");
 
-        if (ifNoneMatchValue != null && ifNoneMatchValue.equals(eTagValue) && eTagValue.equals(eTagUri)) {
-            return Response.notModified().build();
+        if (ifNoneMatchValue != null && ifNoneMatchValue.equals(eTagValue) && eTagValue.equals(etagParam)) {
+            pwmRequest.getPwmResponse().setStatus(304);
+            return ProcessStatus.Halt;
         }
 
-        response.setHeader("ETag", eTagValue);
-        response.setDateHeader("Expires", System.currentTimeMillis() + (maxCacheAgeSeconds * 1000));
-        response.setHeader("Cache-Control", "public, max-age=" + maxCacheAgeSeconds);
+        pwmRequest.getPwmResponse().setHeader(HttpHeader.ETag, eTagValue);
+        pwmRequest.getPwmResponse().setHeader(HttpHeader.Expires, String.valueOf(System.currentTimeMillis() + (maxCacheAgeSeconds * 1000)));
+        pwmRequest.getPwmResponse().setHeader(HttpHeader.Cache_Control, "public, max-age=" + maxCacheAgeSeconds);
 
-        final AppData appData = makeAppData(restRequestBean.getPwmApplication(), restRequestBean.getPwmSession(), request, response, pageUrl);
+        final AppData appData = makeAppData(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), pwmRequest.getHttpServletRequest(), pwmRequest.getPwmResponse().getHttpServletResponse(), pageUrl);
         final RestResultBean restResultBean = new RestResultBean();
         restResultBean.setData(appData);
-        return restResultBean.asJsonResponse();
+        pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
     }
 
-    @GET
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    @Path("/strings/{bundle}")
-    public Response doGetStringData(
-            @PathParam(value = "bundle") final String bundleName
+    @ActionHandler(action = "strings")
+    public ProcessStatus doGetStringsData(final PwmRequest pwmRequest
     )
-            throws PwmUnrecoverableException, IOException, ChaiUnavailableException
-    {
+            throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException {
+        final String bundleName = pwmRequest.readParameterAsString("bundle");
         final int maxCacheAgeSeconds = 60 * 5;
-        final RestRequestBean restRequestBean;
-        try {
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, ServicePermissions.PUBLIC, null);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
 
-        final String eTagValue = makeClientEtag(restRequestBean.getPwmApplication(), restRequestBean.getPwmSession(), request);
-        response.setHeader("ETag",eTagValue);
-        response.setDateHeader("Expires", System.currentTimeMillis() + (maxCacheAgeSeconds * 1000));
-        response.setHeader("Cache-Control", "public, max-age=" + maxCacheAgeSeconds);
+        final String eTagValue = makeClientEtag(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), pwmRequest.getHttpServletRequest());
+
+        pwmRequest.getPwmResponse().setHeader(HttpHeader.ETag, eTagValue);
+        pwmRequest.getPwmResponse().setHeader(HttpHeader.Expires, String.valueOf(System.currentTimeMillis() + (maxCacheAgeSeconds * 1000)));
+        pwmRequest.getPwmResponse().setHeader(HttpHeader.Cache_Control, "public, max-age=" + maxCacheAgeSeconds);
 
         try {
-            final LinkedHashMap<String,String> displayData = new LinkedHashMap<>(makeDisplayData(restRequestBean.getPwmApplication(),
-                    restRequestBean.getPwmSession(), bundleName));
+            final LinkedHashMap<String,String> displayData = new LinkedHashMap<>(makeDisplayData(pwmRequest.getPwmApplication(),
+                    pwmRequest.getPwmSession(), bundleName));
             final RestResultBean restResultBean = new RestResultBean();
             restResultBean.setData(displayData);
-            return restResultBean.asJsonResponse();
+            pwmRequest.outputJsonResult(restResultBean);
         } catch (Exception e) {
             final String errorMSg = "error during rest /strings call for bundle " + bundleName + ", error: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN,errorMSg);
-            return RestResultBean.fromError(errorInformation).asJsonResponse();
+            LOGGER.debug(pwmRequest, errorInformation);
+            pwmRequest.respondWithError(errorInformation);
+        }
+        return ProcessStatus.Halt;
+    }
+
+    @ActionHandler(action = "health")
+    public ProcessStatus restHealthProcessor(final PwmRequest pwmRequest)
+            throws IOException, ServletException, PwmUnrecoverableException
+    {
+        if (pwmRequest.getPwmApplication().getApplicationMode() == PwmApplicationMode.RUNNING) {
+
+            if (!pwmRequest.isAuthenticated()) {
+                final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_AUTHENTICATION_REQUIRED);
+                LOGGER.debug(pwmRequest, errorInformation);
+                pwmRequest.respondWithError(errorInformation);
+                return ProcessStatus.Halt;
+            }
+
+            if (!pwmRequest.getPwmSession().getSessionManager().checkPermission(pwmRequest.getPwmApplication(), Permission.PWMADMIN)) {
+                final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "admin privileges required");
+                LOGGER.debug(pwmRequest, errorInformation);
+                pwmRequest.respondWithError(errorInformation);
+                return ProcessStatus.Halt;
+            }
+        }
+
+        try {
+            final HealthData jsonOutput = RestHealthServer.processGetHealthCheckData(
+                    pwmRequest.getPwmApplication(),
+                    pwmRequest.getLocale(),
+                    false);
+            final RestResultBean restResultBean = new RestResultBean();
+            restResultBean.setData(jsonOutput);
+            pwmRequest.outputJsonResult(restResultBean);
+        } catch (PwmException e) {
+            final ErrorInformation errorInformation = e.getErrorInformation();
+            LOGGER.debug(pwmRequest, errorInformation);
+            pwmRequest.respondWithError(errorInformation);
+        } catch (Exception e) {
+            final String errorMessage = "unexpected error executing web service: " + e.getMessage();
+            final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMessage);
+            LOGGER.debug(pwmRequest, errorInformation);
+            pwmRequest.respondWithError(errorInformation);
+        }
+        return ProcessStatus.Halt;
+    }
+
+    @ActionHandler(action = "ping")
+    public ProcessStatus processPingRequest(final PwmRequest pwmRequest)
+            throws IOException
+    {
+        final PingResponse pingResponse = new PingResponse();
+        pingResponse.setTime(Instant.now());
+        pingResponse.setRuntimeNonce(pwmRequest.getPwmApplication().getRuntimeNonce());
+        pwmRequest.outputJsonResult(new RestResultBean(pingResponse));
+        return ProcessStatus.Halt;
+    }
+
+    public static String makeClientEtag(final PwmRequest pwmRequest)
+            throws PwmUnrecoverableException
+    {
+        return makeClientEtag(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), pwmRequest.getHttpServletRequest());
+    }
+
+    public static String makeClientEtag(
+            final PwmApplication pwmApplication,
+            final PwmSession pwmSession,
+            final HttpServletRequest httpServletRequest
+    )
+            throws PwmUnrecoverableException
+    {
+        final StringBuilder inputString = new StringBuilder();
+        inputString.append(PwmConstants.BUILD_NUMBER);
+        inputString.append(pwmApplication.getStartupTime().toEpochMilli());
+        inputString.append(httpServletRequest.getSession().getMaxInactiveInterval());
+        inputString.append(pwmApplication.getRuntimeNonce());
+
+        if (pwmSession.getSessionStateBean().getLocale() != null) {
+            inputString.append(pwmSession.getSessionStateBean().getLocale());
+        }
+
+        inputString.append(pwmSession.getSessionStateBean().getSessionID());
+        if (pwmSession.isAuthenticated()) {
+            inputString.append(pwmSession.getUserInfo().getUserGuid());
+            inputString.append(pwmSession.getLoginInfoBean().getAuthTime());
         }
+
+        return SecureEngine.hash(inputString.toString(), PwmHashAlgorithm.SHA1).toLowerCase();
     }
 
     private AppData makeAppData(
@@ -316,32 +270,6 @@ public class RestAppDataServer extends AbstractRestServer {
         return appData;
     }
 
-    private Map<String,String> makeDisplayData(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
-            final String bundleName
-    )
-    {
-        Class displayClass = LocaleHelper.classForShortName(bundleName);
-        displayClass = displayClass == null ? Display.class : displayClass;
-
-        final Locale userLocale = pwmSession.getSessionStateBean().getLocale();
-        final Configuration config = pwmApplication.getConfig();
-        final TreeMap<String,String> displayStrings = new TreeMap<>();
-        final ResourceBundle bundle = ResourceBundle.getBundle(displayClass.getName());
-        try {
-            final MacroMachine macroMachine = pwmSession.getSessionManager().getMacroMachine(pwmApplication);
-            for (final String key : new TreeSet<>(Collections.list(bundle.getKeys()))) {
-                String displayValue = LocaleHelper.getLocalizedMessage(userLocale, key, config, displayClass);
-                displayValue = macroMachine.expandMacros(displayValue);
-                displayStrings.put(key, displayValue);
-            }
-        } catch (Exception e) {
-            LOGGER.error(pwmSession,"error expanding macro display value: " + e.getMessage());
-        }
-        return displayStrings;
-    }
-
     private static Map<String,Object> makeClientData(
             final PwmApplication pwmApplication,
             final PwmSession pwmSession,
@@ -461,35 +389,31 @@ public class RestAppDataServer extends AbstractRestServer {
     }
 
 
-    public static String makeClientEtag(final PwmRequest pwmRequest)
-            throws PwmUnrecoverableException
-    {
-        return makeClientEtag(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), pwmRequest.getHttpServletRequest());
-    }
 
-    public static String makeClientEtag(
+    private Map<String,String> makeDisplayData(
             final PwmApplication pwmApplication,
             final PwmSession pwmSession,
-            final HttpServletRequest httpServletRequest
+            final String bundleName
     )
-            throws PwmUnrecoverableException
     {
-        final StringBuilder inputString = new StringBuilder();
-        inputString.append(PwmConstants.BUILD_NUMBER);
-        inputString.append(pwmApplication.getStartupTime().toEpochMilli());
-        inputString.append(httpServletRequest.getSession().getMaxInactiveInterval());
-        inputString.append(pwmApplication.getRuntimeNonce());
-
-        if (pwmSession.getSessionStateBean().getLocale() != null) {
-            inputString.append(pwmSession.getSessionStateBean().getLocale());
-        }
+        Class displayClass = LocaleHelper.classForShortName(bundleName);
+        displayClass = displayClass == null ? Display.class : displayClass;
 
-        inputString.append(pwmSession.getSessionStateBean().getSessionID());
-        if (pwmSession.isAuthenticated()) {
-            inputString.append(pwmSession.getUserInfo().getUserGuid());
-            inputString.append(pwmSession.getLoginInfoBean().getAuthTime());
+        final Locale userLocale = pwmSession.getSessionStateBean().getLocale();
+        final Configuration config = pwmApplication.getConfig();
+        final TreeMap<String,String> displayStrings = new TreeMap<>();
+        final ResourceBundle bundle = ResourceBundle.getBundle(displayClass.getName());
+        try {
+            final MacroMachine macroMachine = pwmSession.getSessionManager().getMacroMachine(pwmApplication);
+            for (final String key : new TreeSet<>(Collections.list(bundle.getKeys()))) {
+                String displayValue = LocaleHelper.getLocalizedMessage(userLocale, key, config, displayClass);
+                displayValue = macroMachine.expandMacros(displayValue);
+                displayStrings.put(key, displayValue);
+            }
+        } catch (Exception e) {
+            LOGGER.error(pwmSession,"error expanding macro display value: " + e.getMessage());
         }
-
-        return SecureEngine.hash(inputString.toString(), PwmHashAlgorithm.SHA1).toLowerCase();
+        return displayStrings;
     }
+
 }

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

@@ -79,6 +79,7 @@ public enum PwmServletDefinition {
     GuestRegistration(password.pwm.http.servlet.GuestRegistrationServlet.class, null),
     SelfDelete(DeleteAccountServlet.class, DeleteAccountBean.class),
 
+    ClientApi(ClientApiServlet.class, null),
     Admin(AdminServlet.class, AdminBean.class),
     ConfigGuide(ConfigGuideServlet.class, ConfigGuideBean.class),
     ConfigEditor(ConfigEditorServlet.class, null),

+ 98 - 0
server/src/main/java/password/pwm/http/servlet/admin/AdminServlet.java

@@ -28,8 +28,10 @@ import password.pwm.Permission;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.bean.UserIdentity;
+import password.pwm.bean.pub.SessionStateInfoBean;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
+import password.pwm.error.PwmException;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.HttpMethod;
@@ -43,6 +45,11 @@ import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.http.servlet.ControlledPwmServlet;
 import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.ldap.search.UserSearchEngine;
+import password.pwm.svc.event.AuditRecord;
+import password.pwm.svc.event.HelpdeskAuditRecord;
+import password.pwm.svc.event.SystemAuditRecord;
+import password.pwm.svc.event.UserAuditRecord;
+import password.pwm.svc.intruder.RecordType;
 import password.pwm.svc.report.ReportColumnFilter;
 import password.pwm.svc.report.ReportCsvUtility;
 import password.pwm.svc.report.ReportService;
@@ -62,10 +69,15 @@ import java.io.OutputStream;
 import java.io.Writer;
 import java.lang.management.ManagementFactory;
 import java.lang.management.ThreadInfo;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.TreeMap;
 
 @WebServlet(
         name = "AdminServlet",
@@ -90,6 +102,9 @@ public class AdminServlet extends ControlledPwmServlet {
         reportStatus(HttpMethod.GET),
         reportSummary(HttpMethod.GET),
         downloadUserDebug(HttpMethod.GET),
+        auditData(HttpMethod.GET),
+        sessionData(HttpMethod.GET),
+        intruderData(HttpMethod.GET),
 
         ;
 
@@ -341,6 +356,80 @@ public class AdminServlet extends ControlledPwmServlet {
         return ProcessStatus.Halt;
     }
 
+    @ActionHandler(action = "auditData")
+    private ProcessStatus restAuditDataHandler(final PwmRequest pwmRequest)
+            throws ChaiUnavailableException, PwmUnrecoverableException, IOException
+    {
+        final int max = readMaxParameter(pwmRequest, 1000, 10* 1000);
+        final ArrayList<UserAuditRecord> userRecords = new ArrayList<>();
+        final ArrayList<HelpdeskAuditRecord> helpdeskRecords = new ArrayList<>();
+        final ArrayList<SystemAuditRecord> systemRecords = new ArrayList<>();
+        final Iterator<AuditRecord> iterator = pwmRequest.getPwmApplication().getAuditManager().readVault();
+        int counter = 0;
+        while (iterator.hasNext() && counter <= max) {
+            final AuditRecord loopRecord = iterator.next();
+            counter++;
+            if (loopRecord instanceof SystemAuditRecord) {
+                systemRecords.add((SystemAuditRecord)loopRecord);
+            } else if (loopRecord instanceof HelpdeskAuditRecord) {
+                helpdeskRecords.add((HelpdeskAuditRecord)loopRecord);
+            } else if (loopRecord instanceof UserAuditRecord) {
+                userRecords.add((UserAuditRecord)loopRecord);
+            }
+        }
+        final HashMap<String,List> outputMap = new HashMap<>();
+        outputMap.put("user",userRecords);
+        outputMap.put("helpdesk",helpdeskRecords);
+        outputMap.put("system",systemRecords);
+
+        final RestResultBean restResultBean = new RestResultBean();
+        restResultBean.setData(outputMap);
+        LOGGER.debug(pwmRequest.getPwmSession(),"output " + counter + " audit records.");
+        pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
+    }
+
+    @ActionHandler(action = "sessionData")
+    private ProcessStatus restSessionDataHandler(final PwmRequest pwmRequest)
+            throws ChaiUnavailableException, PwmUnrecoverableException, IOException {
+        final int max = readMaxParameter(pwmRequest, 1000, 10* 1000);
+
+        final ArrayList<SessionStateInfoBean> gridData = new ArrayList<>();
+        int counter = 0;
+        final Iterator<SessionStateInfoBean> infos = pwmRequest.getPwmApplication().getSessionTrackService().getSessionInfoIterator();
+        while (counter < max && infos.hasNext()) {
+            gridData.add(infos.next());
+            counter++;
+        }
+        final RestResultBean restResultBean = new RestResultBean();
+        restResultBean.setData(gridData);
+        pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
+    }
+
+    @ActionHandler(action = "intruderData")
+    private ProcessStatus restIntruderDataHandler(final PwmRequest pwmRequest)
+            throws ChaiUnavailableException, PwmUnrecoverableException, IOException {
+        final int max = readMaxParameter(pwmRequest, 1000, 10* 1000);
+
+        final TreeMap<String,Object> returnData = new TreeMap<>();
+        try {
+            for (final RecordType recordType : RecordType.values()) {
+                returnData.put(recordType.toString(),pwmRequest.getPwmApplication().getIntruderManager().getRecords(recordType, max));
+            }
+        } catch (PwmException e) {
+            final ErrorInformation errorInfo = new ErrorInformation(PwmError.ERROR_UNKNOWN,e.getMessage());
+            LOGGER.debug(pwmRequest, errorInfo);
+            pwmRequest.outputJsonResult(RestResultBean.fromError(errorInfo));
+        }
+
+        final RestResultBean restResultBean = new RestResultBean();
+        restResultBean.setData(returnData);
+        pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
+    }
+
+
     private void processDebugUserSearch(final PwmRequest pwmRequest)
             throws PwmUnrecoverableException
     {
@@ -394,6 +483,13 @@ public class AdminServlet extends ControlledPwmServlet {
         pwmRequest.sendRedirect(pwmRequest.getContextPath() + PwmServletDefinition.Admin.servletUrl() + Page.dashboard.getUrlSuffix());
     }
 
+    private static int readMaxParameter(final PwmRequest pwmRequest, final int defaultValue, final int maxValue)
+            throws PwmUnrecoverableException
+    {
+            final String stringMax = pwmRequest.readParameterAsString("maximum",String.valueOf(defaultValue));
+            return Math.max(Integer.parseInt(stringMax), maxValue);
+    }
+
     public enum Page {
         dashboard(JspUrl.ADMIN_DASHBOARD,"/dashboard"),
         analysis(JspUrl.ADMIN_ANALYSIS,"/analysis"),
@@ -432,4 +528,6 @@ public class AdminServlet extends ControlledPwmServlet {
             return null;
         }
     }
+
+
 }

+ 2 - 2
server/src/main/java/password/pwm/http/tag/value/PwmValue.java

@@ -31,11 +31,11 @@ import password.pwm.config.PwmSetting;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.IdleTimeoutCalculator;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.servlet.ClientApiServlet;
 import password.pwm.i18n.Admin;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroMachine;
-import password.pwm.ws.server.rest.RestAppDataServer;
 
 import javax.servlet.jsp.JspPage;
 import javax.servlet.jsp.PageContext;
@@ -199,7 +199,7 @@ public enum PwmValue {
     static class ClientETag implements ValueOutput {
         @Override
         public String valueOutput(final PwmRequest pwmRequest, final PageContext pageContext) throws ChaiUnavailableException, PwmUnrecoverableException {
-            return RestAppDataServer.makeClientEtag(pwmRequest);
+            return ClientApiServlet.makeClientEtag(pwmRequest);
         }
     }
 

+ 1 - 1
server/src/main/java/password/pwm/ws/server/rest/RestHealthServer.java

@@ -148,7 +148,7 @@ public class RestHealthServer extends AbstractRestServer {
                 : HealthMonitor.CheckTimeliness.CurrentButNotAncient;
     }
 
-    private static HealthData processGetHealthCheckData(
+    public static HealthData processGetHealthCheckData(
             final PwmApplication pwmApplication,
             final Locale locale,
             final boolean refreshImmediate

+ 0 - 47
server/src/main/java/password/pwm/ws/server/rest/RestPingServer.java

@@ -1,47 +0,0 @@
-package password.pwm.ws.server.rest;
-
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-import password.pwm.PwmConstants;
-import password.pwm.http.HttpMethod;
-import password.pwm.ws.server.PwmRestServlet;
-import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.StandaloneRestRequestBean;
-
-import javax.servlet.annotation.WebServlet;
-import java.io.Serializable;
-import java.time.Instant;
-import java.util.Collections;
-
-@WebServlet(
-        name="RestPingServer",
-        urlPatterns={
-                PwmConstants.URL_PREFIX_PUBLIC + PwmConstants.URL_PREFIX_REST + "/ping",
-        }
-)
-
-public class RestPingServer extends PwmRestServlet {
-
-    @Getter
-    @Setter
-    @NoArgsConstructor
-    public static class PingResponse implements Serializable {
-        private Instant time;
-        private String runtimeNonce;
-    }
-
-    @Override
-    public RestResultBean invokeWebService(final StandaloneRestRequestBean standaloneRestRequestBean) {
-        final PingResponse pingResponse = new PingResponse();
-        pingResponse.setTime(Instant.now());
-        pingResponse.setRuntimeNonce(standaloneRestRequestBean.getPwmApplication().getRuntimeNonce());
-        return new RestResultBean(pingResponse);
-    }
-
-    @Override
-    public PwmRestServlet.ServiceInfo getServiceInfo() {
-        return new ServiceInfo(Collections.singleton(HttpMethod.GET));
-    }
-
-}

+ 7 - 4
server/src/main/webapp/public/resources/js/admin.js

@@ -348,7 +348,8 @@ PWM_ADMIN.refreshActiveSessionGrid=function() {
         grid.refresh();
 
         var maximum = PWM_MAIN.getObject('maxActiveSessionResults').value;
-        var url = PWM_GLOBAL['url-restservice'] + "/app-data/session?maximum=" + maximum;
+        var url = PWM_MAIN.addParamToUrl(window.location.href,"processAction", "sessionData");
+        url = PWM_MAIN.addParamToUrl(url,'maximum',maximum);
         var loadFunction = function(data) {
             grid.renderArray(data['data']);
             grid.set("sort", { attribute : 'createTime', ascending: false, descending: true });
@@ -405,7 +406,8 @@ PWM_ADMIN.refreshIntruderGrid=function() {
     } catch (e) {
         maximum = 1000;
     }
-    var url = PWM_GLOBAL['url-restservice'] + "/app-data/intruder?maximum=" + maximum;
+    var url = PWM_MAIN.addParamToUrl(window.location.href,"processAction", "intruderData");
+    url = PWM_MAIN.addParamToUrl(url,'maximum',maximum);
     var loadFunction = function(data) {
         for (var i = 0; i < PWM_VAR['intruderRecordTypes'].length; i++) {
             var recordType = PWM_VAR['intruderRecordTypes'][i];
@@ -511,7 +513,8 @@ PWM_ADMIN.refreshAuditGridData=function(maximum) {
     if (!maximum) {
         maximum = 1000;
     }
-    var url = PWM_GLOBAL['url-restservice'] + "/app-data/audit?maximum=" + maximum;
+    var url = PWM_MAIN.addParamToUrl(window.location.href,"processAction", "auditData");
+    url = PWM_MAIN.addParamToUrl(url,'maximum',maximum);
     var loadFunction = function(data) {
         PWM_VAR['auditUserGrid'].renderArray(data['data']['user']);
         PWM_VAR['auditUserGrid'].set("sort", { attribute : 'timestamp', ascending: false, descending: true });
@@ -647,7 +650,7 @@ PWM_ADMIN.showAppHealth = function(parentDivID, options, refreshNow) {
 
     var inputOpts = options || PWM_GLOBAL['showPwmHealthOptions'] || {};
     PWM_GLOBAL['showPwmHealthOptions'] = options;
-    var refreshUrl = inputOpts['sourceUrl'] || PWM_GLOBAL['url-restservice'] + "/health";
+    var refreshUrl = inputOpts['sourceUrl'] || PWM_GLOBAL['url-context'] + "/public/api?processAction=health";
     var showRefresh = inputOpts['showRefresh'];
     var showTimestamp = inputOpts['showTimestamp'];
     var refreshTime = inputOpts['refreshTime'] || 60 * 1000;

+ 4 - 4
server/src/main/webapp/public/resources/js/configmanager.js

@@ -99,8 +99,8 @@ PWM_CONFIG.waitForRestart=function(options) {
         }, pingCycleTimeMs);
         console.log('Waiting for server restart, unable to contact server: ' + error);
     };
-    var url = PWM_GLOBAL['url-restservice'] + "/ping";
-    PWM_MAIN.ajaxRequest(url,loadFunction,{errorFunction:errorFunction,method:'GET'});
+    var url = PWM_GLOBAL['url-context'] + "/public/api?processAction=ping";
+    PWM_MAIN.ajaxRequest(url,loadFunction,{errorFunction:errorFunction,method:'GET',preventCache:true});
 };
 
 PWM_CONFIG.startNewConfigurationEditor=function(template) {
@@ -199,7 +199,7 @@ PWM_CONFIG.openLogViewer=function(level) {
 };
 
 PWM_CONFIG.showHeaderHealth = function() {
-    var refreshUrl = PWM_GLOBAL['url-restservice'] + "/health";
+    var refreshUrl = PWM_GLOBAL['url-context'] + "/public/api?processAction=health";
     var parentDiv = PWM_MAIN.getObject('panel-header-healthData');
     if (!parentDiv) {
         return;
@@ -326,7 +326,7 @@ PWM_CONFIG.heartbeatCheck = function() {
     var errorFunction = function(e) {
         handleErrorFunction('I/O error communicating with server.');
     };
-    var url = PWM_GLOBAL['url-restservice'] + "/app-data/client?heartbeat=true";
+    var url = PWM_GLOBAL['url-context'] + "/public/api?processAction=clientData&heartbeat=true";
     url = PWM_MAIN.addParamToUrl(url,'pageUrl',window.location.href);
     PWM_MAIN.ajaxRequest(url,loadFunction,{errorFunction:errorFunction,method:'GET'});
 };

+ 2 - 2
server/src/main/webapp/public/resources/js/main.js

@@ -77,7 +77,7 @@ PWM_MAIN.pageLoadHandler = function() {
 
 PWM_MAIN.loadClientData=function(completeFunction) {
     PWM_GLOBAL['app-data-client-retry-count'] = PWM_GLOBAL['app-data-client-retry-count'] + 1;
-    var url = PWM_GLOBAL['url-context'] + "/public/rest/app-data/client?etag=" + PWM_GLOBAL['clientEtag'];
+    var url = PWM_GLOBAL['url-context'] + "/public/api?processAction=clientData&etag=" + PWM_GLOBAL['clientEtag'];
     url = PWM_MAIN.addParamToUrl(url,'pageUrl',window.location.href);
     var loadFunction = function(data) {
         for (var globalProp in data['data']['PWM_GLOBAL']) {
@@ -96,7 +96,7 @@ PWM_MAIN.loadClientData=function(completeFunction) {
 };
 
 PWM_MAIN.loadLocaleBundle = function(bundleName, completeFunction) {
-    var clientConfigUrl = PWM_GLOBAL['url-context'] + "/public/rest/app-data/strings/" + bundleName;
+    var clientConfigUrl = PWM_GLOBAL['url-context'] + "/public/api?processAction=strings&bundle=" + bundleName;
     clientConfigUrl = PWM_MAIN.addParamToUrl(clientConfigUrl,'etag',PWM_GLOBAL['clientEtag']);
     var loadFunction = function(data){
         if (data['error'] === true) {