Vojtech Szocs has uploaded a new change for review. Change subject: webadmin: Fix UI plugin REST API HttpSession refresh issue ......................................................................
webadmin: Fix UI plugin REST API HttpSession refresh issue TODO(vszocs) write commit msg after getting more sleep Change-Id: I2ffd3198239040f67313238a837af98db16e79ea Bug-Url: https://bugzilla.redhat.com/1172726 Bug-Url: https://bugzilla.redhat.com/1168842 Signed-off-by: Vojtech Szocs <vsz...@redhat.com> --- M backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/FiltersHelper.java A backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/RefreshSessionFilter.java M backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml M frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManager.java M frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java 5 files changed, 120 insertions(+), 15 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/22/36622/1 diff --git a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/FiltersHelper.java b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/FiltersHelper.java index 924d805..7679c0c 100644 --- a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/FiltersHelper.java +++ b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/FiltersHelper.java @@ -24,6 +24,7 @@ public static final String HEADER_PREFER = "Prefer"; public static final String HEADER_JSESSIONID_COOKIE = "JSESSIONID"; public static final String HEADER_ENGINE_AUTH_TOKEN = "OVIRT-INTERNAL-ENGINE-AUTH-TOKEN"; + public static final String HEADER_ENGINE_REFRESH = "X-OVIRT-INTERNAL-ENGINE-REFRESH"; } public static final int PREFER_NEW_AUTH = (1<<0); diff --git a/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/RefreshSessionFilter.java b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/RefreshSessionFilter.java new file mode 100644 index 0000000..688b3dd --- /dev/null +++ b/backend/manager/modules/aaa/src/main/java/org/ovirt/engine/core/aaa/filters/RefreshSessionFilter.java @@ -0,0 +1,39 @@ +package org.ovirt.engine.core.aaa.filters; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + +import org.ovirt.engine.core.aaa.filters.FiltersHelper.Constants; + +public class RefreshSessionFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, + ServletException { + HttpServletRequest req = (HttpServletRequest) request; + + if (FiltersHelper.isAuthenticated(req) && req.getHeader(Constants.HEADER_ENGINE_REFRESH) != null) { + // Do nothing, this request was meant to only refresh the given HttpSession's expiry timer + response.setContentType("text/plain"); + response.setContentLength(0); + } else { + chain.doFilter(request, response); + } + } + + @Override + public void destroy() { + } + +} diff --git a/backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml b/backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml index 9a108c4..d3eac0f 100644 --- a/backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml +++ b/backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml @@ -119,6 +119,15 @@ <url-pattern>/*</url-pattern> </filter-mapping> + <filter> + <filter-name>RefreshSessionFilter</filter-name> + <filter-class>org.ovirt.engine.core.aaa.filters.RefreshSessionFilter</filter-class> + </filter> + <filter-mapping> + <filter-name>RefreshSessionFilter</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + <!-- confidentiality --> <security-constraint> <web-resource-collection> diff --git a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManager.java b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManager.java index 2c00dd2..2058ec8 100644 --- a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManager.java +++ b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/communication/VdcOperationManager.java @@ -14,6 +14,12 @@ */ public class VdcOperationManager { + public interface OperationCallback { + void operationExecuted(VdcOperation<?, ?> operation); + } + + private final List<OperationCallback> operationCallbacks = new ArrayList<>(); + /** * The operation queue. It can hold any kind of VdcOperation. */ @@ -36,6 +42,10 @@ @Inject public VdcOperationManager(final OperationProcessor operationProcessor) { this.processor = operationProcessor; + } + + public void addOperationCallback(OperationCallback callback) { + operationCallbacks.add(callback); } /** @@ -73,6 +83,10 @@ if (isAllowedToExecute) { if (operationCanBeAdded && operationQueue.add(operation)) { processor.processOperation(this); + + for (OperationCallback callback : operationCallbacks) { + callback.operationExecuted(operation); + } } } @@ -169,4 +183,5 @@ public void retrieveFromHttpSession(final String key, final StorageCallback callback) { processor.retrieveFromHttpSession(key, callback); } + } diff --git a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java index d162ce8..7d50ab5 100644 --- a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java +++ b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/plugin/restapi/RestApiSessionManager.java @@ -3,9 +3,13 @@ import java.util.logging.Level; import java.util.logging.Logger; +import org.ovirt.engine.core.common.queries.VdcQueryParametersBase; import org.ovirt.engine.ui.common.utils.HttpUtils; import org.ovirt.engine.ui.frontend.Frontend; import org.ovirt.engine.ui.frontend.communication.StorageCallback; +import org.ovirt.engine.ui.frontend.communication.VdcOperation; +import org.ovirt.engine.ui.frontend.communication.VdcOperationManager; +import org.ovirt.engine.ui.frontend.communication.VdcOperationManager.OperationCallback; import org.ovirt.engine.ui.frontend.utils.BaseContextPathData; import com.google.gwt.core.client.Scheduler; @@ -38,7 +42,7 @@ * <p> * Triggers {@link RestApiSessionAcquiredEvent} upon acquiring or reusing REST API session. */ -public class RestApiSessionManager { +public class RestApiSessionManager implements OperationCallback { private static class RestApiRequestCallback implements RequestCallback { @@ -71,6 +75,7 @@ private static final String SESSION_ID_HEADER = "JSESSIONID"; //$NON-NLS-1$ private static final String CSRF_HEADER = "JSESSIONID"; //$NON-NLS-1$ private static final String ENGINE_AUTH_TOKEN_HEADER = "OVIRT-INTERNAL-ENGINE-AUTH-TOKEN"; //$NON-NLS-1$ + private static final String ENGINE_REFRESH_HEADER = "X-OVIRT-INTERNAL-ENGINE-REFRESH"; //$NON-NLS-1$ private static final String SESSION_ID_KEY = "RestApiSessionId"; //$NON-NLS-1$ private static final String DEFAULT_SESSION_TIMEOUT = "30"; //$NON-NLS-1$ @@ -84,23 +89,45 @@ private String restApiSessionTimeout = DEFAULT_SESSION_TIMEOUT; private String restApiSessionId; + private boolean refreshRestApiSession = false; + @Inject - public RestApiSessionManager(EventBus eventBus) { + public RestApiSessionManager(EventBus eventBus, VdcOperationManager operationManager) { this.eventBus = eventBus; // Note that the slash at the end of the URL is not just a whim. With the trailing slash the browser will only // send authentication headers to URLs ending in api/, otherwise it will send them to URLs ending in /, and // this causes problems in other applications, for example in the reports application. this.restApiBaseUrl = BaseContextPathData.getInstance().getPath() + "api/"; //$NON-NLS-1$ + + operationManager.addOperationCallback(this); + } + + @Override + public void operationExecuted(VdcOperation<?, ?> operation) { + if (restApiSessionId != null && engineSessionRefreshed(operation)) { + refreshRestApiSession = true; + } + } + + boolean engineSessionRefreshed(VdcOperation<?, ?> operation) { + // Actions always refresh the Engine session + if (operation.isAction()) { + return true; + } + + // Queries optionally refresh the Engine session + else if (((VdcQueryParametersBase) operation.getParameter()).getRefresh()) { + return true; + } + + return false; } public void setSessionTimeout(String sessionTimeout) { this.restApiSessionTimeout = sessionTimeout; } - /** - * Build HTTP request to keep-alive existing REST API session. - */ RequestBuilder createRequest() { RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, restApiBaseUrl); @@ -121,7 +148,7 @@ /** * Build HTTP request to acquire new REST API session. */ - RequestBuilder createRequest(String engineAuthToken) { + RequestBuilder createNewSessionRequest(String engineAuthToken) { RequestBuilder builder = createRequest(); // Enforce expiry of existing session when acquiring new session @@ -130,6 +157,18 @@ // Map this (physical) REST API session to current user's (logical) Engine session builder.setHeader(ENGINE_AUTH_TOKEN_HEADER, engineAuthToken); + + return builder; + } + + /** + * Build HTTP request to keep-alive existing REST API session. + */ + RequestBuilder createKeepAliveRequest() { + RequestBuilder builder = createRequest(); + + // This request shouldn't do anything other than refresh the REST API session + builder.setHeader(ENGINE_REFRESH_HEADER, "refresh-only"); //$NON-NLS-1$ return builder; } @@ -146,16 +185,18 @@ Scheduler.get().scheduleFixedDelay(new RepeatingCommand() { @Override public boolean execute() { - if (restApiSessionId != null) { - // The browser takes care of sending JSESSIONID cookie for this request automatically - sendRequest(createRequest(), new RestApiRequestCallback()); + boolean sessionInUse = restApiSessionId != null; - // The session is still in use, proceed with the heartbeat - return true; - } else { - // The session has been released, cancel the heartbeat - return false; + if (sessionInUse && refreshRestApiSession) { + // The browser takes care of sending JSESSIONID cookie for this request automatically + sendRequest(createKeepAliveRequest(), new RestApiRequestCallback()); + + // Reset the refresh flag + refreshRestApiSession = false; } + + // Proceed with the heartbeat only when the session is still in use + return sessionInUse; } }, SESSION_HEARTBEAT_MS); } @@ -164,7 +205,7 @@ * Acquires new REST API session that maps to current user's Engine session. */ public void acquireSession(String engineAuthToken) { - sendRequest(createRequest(engineAuthToken), new RestApiRequestCallback() { + sendRequest(createNewSessionRequest(engineAuthToken), new RestApiRequestCallback() { @Override protected void processResponse(Response response) { // Obtain session ID from response header, as we're unable to access the -- To view, visit http://gerrit.ovirt.org/36622 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I2ffd3198239040f67313238a837af98db16e79ea Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: master Gerrit-Owner: Vojtech Szocs <vsz...@redhat.com> _______________________________________________ Engine-patches mailing list Engine-patches@ovirt.org http://lists.ovirt.org/mailman/listinfo/engine-patches