Skip to content

Commit

Permalink
feat(maintenance): redirect to 503 page when platform is in maintenance
Browse files Browse the repository at this point in the history
* add maintenance state check in authentication filter
* add 503 jsp page that will be used when App directory error-503 page is not available

Covers [DEV-479](https://bonitasoft.atlassian.net/browse/DEV-479)
  • Loading branch information
abirembaut committed Sep 5, 2023
1 parent e0d6af7 commit 9beaa40
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
import org.bonitasoft.console.common.server.login.utils.RedirectUrl;
import org.bonitasoft.console.common.server.login.utils.RedirectUrlBuilder;
import org.bonitasoft.console.common.server.utils.SessionUtil;
import org.bonitasoft.engine.api.TenantAPIAccessor;
import org.bonitasoft.engine.api.TenantAdministrationAPI;
import org.bonitasoft.engine.exception.BonitaException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -47,8 +50,6 @@ public class AuthenticationFilter extends ExcludingPatternFilter {

protected static final String REDIRECT_PARAM = "redirectWhenUnauthorized";

protected static final String MAINTENANCE_JSP = "/maintenance.jsp";

protected static final String USER_NOT_FOUND_JSP = "/usernotfound.jsp";

protected boolean redirectWhenUnauthorized;
Expand Down Expand Up @@ -108,10 +109,12 @@ protected void doAuthenticationFiltering(final HttpServletRequestAccessor reques
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
} catch (TenantIsPausedException e) {
handleTenantPausedException(requestAccessor, response, e);
} catch (PlatformUnderMaintenanceException e) {
handlePlatformUnderMaintenanceException(requestAccessor, response, e);
} catch (final EngineUserNotFoundOrInactive e) {
handleUserNotFoundOrInactiveException(requestAccessor, response, e);
} catch (BonitaException e) {
handleBonitaException(requestAccessor, response, e);
}
}

Expand All @@ -127,17 +130,39 @@ protected boolean isAccessingPlatformAPIWithAPISession(final HttpServletRequestA
*/
protected boolean isAuthorized(final HttpServletRequestAccessor requestAccessor,
final HttpServletResponse response,
final FilterChain chain) throws ServletException, IOException {
final FilterChain chain)
throws ServletException, IOException, PlatformUnderMaintenanceException, BonitaException {

for (final AuthenticationRule rule : getRules()) {
if (rule.doAuthorize(requestAccessor, response)) {
checkPlatformMaintenanceState(requestAccessor);
rule.proceedWithRequest(chain, requestAccessor.asHttpServletRequest(), response);
return true;
}
}
return false;
}

protected void checkPlatformMaintenanceState(final HttpServletRequestAccessor requestAccessor)
throws PlatformUnderMaintenanceException, BonitaException {
try {
if (isPlaformInMaintenance(requestAccessor)) {
throw new PlatformUnderMaintenanceException("Platform is under Maintenance");
}
} catch (BonitaException e) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Error while checking platform maintenance state : " + e.getMessage(), e);
}
throw e;
}
}

protected boolean isPlaformInMaintenance(HttpServletRequestAccessor requestAccessor) throws BonitaException {
TenantAdministrationAPI tenantAdministrationAPI = TenantAPIAccessor
.getTenantAdministrationAPI(requestAccessor.getApiSession());
return tenantAdministrationAPI.isPaused();
}

protected void handleUserNotFoundOrInactiveException(final HttpServletRequestAccessor requestAccessor,
final HttpServletResponse response,
final EngineUserNotFoundOrInactive e) throws ServletException {
Expand All @@ -151,19 +176,32 @@ protected void handleUserNotFoundOrInactiveException(final HttpServletRequestAcc
}
}

protected void handleTenantPausedException(final HttpServletRequestAccessor requestAccessor,
protected void handlePlatformUnderMaintenanceException(final HttpServletRequestAccessor requestAccessor,
final HttpServletResponse response,
final TenantIsPausedException e) throws ServletException {
final PlatformUnderMaintenanceException e) throws IOException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("redirection to maintenance page : " + e.getMessage(), e);
LOGGER.debug("Redirection to maintenance page : " + e.getMessage(), e);
}
if (redirectWhenUnauthorized && requestAccessor.asHttpServletRequest().getMethod().equals("GET")) {
redirectTo(requestAccessor, response, MAINTENANCE_JSP);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Platform is under maintenance");
} else {
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
}
}

protected void handleBonitaException(final HttpServletRequestAccessor requestAccessor,
final HttpServletResponse response,
final BonitaException e) throws IOException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("redirection to 500 error page : " + e.getMessage(), e);
}
if (redirectWhenUnauthorized && requestAccessor.asHttpServletRequest().getMethod().equals("GET")) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} else {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}

/**
* manage redirection to maintenance page
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
**/
package org.bonitasoft.console.common.server.login.filter;

public class TenantIsPausedException extends RuntimeException {
public class PlatformUnderMaintenanceException extends RuntimeException {

private static final long serialVersionUID = 4836182063504328577L;

public TenantIsPausedException(final String message) {
public PlatformUnderMaintenanceException(final String message) {
super(message);
}

Expand Down
41 changes: 41 additions & 0 deletions bpm/bonita-web-server/src/main/webapp/503.jsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<%-- Copyright (C) 2023 BonitaSoft S.A.
BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble
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.0 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, see <http://www.gnu.org/licenses/>. --%>
<%@page contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html"><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Error 500</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/bootstrap.min.css">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/error-style.css">
</head>
<body>
<div class="main-container">
<div class="error-title">
<h1 class="text-danger">Under maintenance...</h1>
<h2 class="text-danger">Please try again later.</h2>
</div>
<div class="illustration-container">
<img alt="503 error" src="${pageContext.request.contextPath}/images/503.svg">
</div>
<div class="error-message">
<h2 class="text-primary">The server is currently unable to handle this request due to scheduled maintenance.</h2>
<p class="text-primary">503 Service Unavailable</p>
</div>
</div>
</div>
</body>
</html>
4 changes: 4 additions & 0 deletions bpm/bonita-web-server/src/main/webapp/WEB-INF/web.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
<error-code>500</error-code>
<location>/error/500</location>
</error-page>
<error-page>
<error-code>503</error-code>
<location>/error/503</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/error/403</location>
Expand Down
1 change: 1 addition & 0 deletions bpm/bonita-web-server/src/main/webapp/images/503.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@
import java.util.List;
import java.util.regex.Pattern;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
Expand Down Expand Up @@ -79,15 +75,15 @@ public class AuthenticationFilterTest {
@Before
public void setUp() throws Exception {
initMocks(this);
request = spy(new HttpServletRequestAccessor(httpRequest));
doReturn(httpSession).when(httpRequest).getSession();
doReturn(authenticationManager).when(authenticationFilter).getAuthenticationManager();
when(httpRequest.getRequestURL()).thenReturn(new StringBuffer());
when(httpRequest.getMethod()).thenReturn("GET");
when(servletContext.getContextPath()).thenReturn("");
when(filterConfig.getServletContext()).thenReturn(servletContext);
when(filterConfig.getInitParameterNames()).thenReturn(Collections.emptyEnumeration());

request = spy(new HttpServletRequestAccessor(httpRequest));
doReturn(false).when(authenticationFilter).isPlaformInMaintenance(request);

//remove default rules (already logged in) as they have their own tests
authenticationFilter.getRules().clear();
Expand Down Expand Up @@ -240,26 +236,44 @@ public void testFailedLoginOnDoFiltering() throws Exception {
}

@Test
public void testTenantIsPausedOnDoFiltering() throws Exception {
public void testTenantIsPausedExceptionOnDoFiltering() throws Exception {
when(httpRequest.getServletPath()).thenReturn("/apps");
when(httpRequest.getPathInfo()).thenReturn("/app/home");
PlatformUnderMaintenanceException platformUnderMaintenanceException = new PlatformUnderMaintenanceException(
"login failed");
authenticationFilter.addRule(createThrowingExceptionRule(platformUnderMaintenanceException));

authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);

verify(authenticationManager, never()).getLoginPageURL(eq(request), anyString());
verify(chain, never()).doFilter(httpRequest, httpResponse);
verify(authenticationFilter).handlePlatformUnderMaintenanceException(request, httpResponse,
platformUnderMaintenanceException);
verify(httpResponse).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE), anyString());
}

@Test
public void testPlatformInMaintenance() throws Exception {
when(httpRequest.getServletPath()).thenReturn("/apps");
when(httpRequest.getPathInfo()).thenReturn("/app/home");
TenantIsPausedException tenantIsPausedException = new TenantIsPausedException("login failed");
authenticationFilter.addRule(createThrowingExceptionRule(tenantIsPausedException));
doReturn(true).when(authenticationFilter).isPlaformInMaintenance(request);
authenticationFilter.addRule(createPassingRule());

authenticationFilter.doAuthenticationFiltering(request, httpResponse, chain);

verify(authenticationManager, never()).getLoginPageURL(eq(request), anyString());
verify(chain, never()).doFilter(httpRequest, httpResponse);
verify(authenticationFilter).handleTenantPausedException(request, httpResponse, tenantIsPausedException);
verify(authenticationFilter).redirectTo(request, httpResponse, AuthenticationFilter.MAINTENANCE_JSP);
verify(authenticationFilter).handlePlatformUnderMaintenanceException(eq(request), eq(httpResponse),
any(PlatformUnderMaintenanceException.class));
verify(httpResponse).sendError(eq(HttpServletResponse.SC_SERVICE_UNAVAILABLE), anyString());
}

@Test
public void testRedirectTo() throws Exception {
final String context = "/bonita";
when(httpRequest.getContextPath()).thenReturn(context);
authenticationFilter.redirectTo(request, httpResponse, AuthenticationFilter.MAINTENANCE_JSP);
verify(httpResponse, times(1)).sendRedirect(context + AuthenticationFilter.MAINTENANCE_JSP);
authenticationFilter.redirectTo(request, httpResponse, AuthenticationFilter.USER_NOT_FOUND_JSP);
verify(httpResponse, times(1)).sendRedirect(context + AuthenticationFilter.USER_NOT_FOUND_JSP);
verify(httpRequest, times(1)).getContextPath();
}

Expand Down

0 comments on commit 9beaa40

Please sign in to comment.