
com.appslandia.plum.base.InitializerHandler Maven / Gradle / Ivy
// The MIT License (MIT)
// Copyright © 2015 AppsLandia. All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.appslandia.plum.base;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import javax.inject.Inject;
import javax.servlet.DispatcherType;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.appslandia.common.logging.AppLogger;
import com.appslandia.common.utils.AssertUtils;
import com.appslandia.plum.utils.ServletUtils;
/**
*
* @author Loc Ha
*
*/
public class InitializerHandler extends HttpFilter {
private static final long serialVersionUID = 1L;
@Inject
protected AppConfig appConfig;
@Inject
protected AppLogger appLogger;
@Inject
protected HeaderPolicyProvider headerPolicyProvider;
@Inject
protected CorsPolicyHandler corsPolicyHandler;
@Inject
protected CorsPolicyProvider corsPolicyProvider;
@Inject
protected AuthHandlerProvider authHandlerProvider;
@Inject
protected AuthorizePolicyProvider authorizePolicyProvider;
@Inject
protected ExceptionHandler exceptionHandler;
@Inject
protected RequestContextParser requestContextParser;
protected void initialize(HttpServletRequest request, HttpServletResponse response, RequestContext requestContext) throws Exception {
if (request.getCharacterEncoding() == null) {
request.setCharacterEncoding(StandardCharsets.UTF_8.name());
}
for (String policy : this.appConfig.getStringArray(AppConfig.CONFIG_HEADER_POLICIES)) {
this.headerPolicyProvider.getHeaderPolicy(policy).writePolicy(request, response);
}
}
protected void redirectHttps(HttpServletRequest request, HttpServletResponse response, RequestContext requestContext) throws Exception {
int status = this.appConfig.getRequiredBool(AppConfig.CONFIG_HTTPS_REDIRECT_PERMANENTLY) ? HttpServletResponse.SC_MOVED_PERMANENTLY
: HttpServletResponse.SC_MOVED_TEMPORARILY;
String url = ServletUtils.getSecureUrl(request, this.appConfig.getHttpsPort());
ServletUtils.sendRedirect(response, this.appConfig.isEnableSession() ? response.encodeRedirectURL(url) : url, status);
}
protected String getCrossOrigin(HttpServletRequest request) {
String origin = request.getHeader(CorsPolicyHandler.HEADER_ORIGIN);
if (origin == null) {
return null;
}
StringBuilder url = new StringBuilder(request.getScheme()).append("://").append(request.getServerName());
if (request.isSecure()) {
if (this.appConfig.getHttpsPort() != 443) {
url.append(':').append(this.appConfig.getHttpsPort());
}
} else {
if (this.appConfig.getHttpPort() != 80) {
url.append(':').append(this.appConfig.getHttpPort());
}
}
if (origin.equals(url.toString())) {
return null;
}
return origin;
}
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
AssertUtils.assertTrue(request.getDispatcherType().equals(DispatcherType.REQUEST));
try {
// RequestContext
RequestContext requestContext = this.requestContextParser.parse(request, response);
// Initialize
initialize(request, response, requestContext);
// Not Found?
if (requestContext.getActionDesc() == null) {
throw new NotFoundException(Resources.ERROR_NOT_FOUND, requestContext.getResources());
}
// Allow Method?
if (!requestContext.getActionDesc().getAllowMethods().contains(request.getMethod())) {
throw new HttpException(HttpServletResponse.SC_METHOD_NOT_ALLOWED, Resources.ERROR_METHOD_NOT_ALLOWED, requestContext.getResources());
}
// Consume Type
if (requestContext.getActionDesc().getConsumeType() != null) {
if (!ServletUtils.allowContentType(request.getContentType(), requestContext.getActionDesc().getConsumeType().value())) {
throw new HttpException(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, Resources.ERROR_UNSUPPORTED_MEDIA_TYPE, requestContext.getResources());
}
}
// HTTPS
if ((requestContext.getActionDesc().getEnableHttps() != null) && !request.isSecure()) {
if (!requestContext.isGetOrHead()) {
throw new HttpException(HttpServletResponse.SC_FORBIDDEN, Resources.ERROR_HTTPS_REQUIRED, requestContext.getResources());
}
redirectHttps(request, response, requestContext);
return;
}
// HTTP OPTIONS
if (HttpMethod.OPTIONS.equals(request.getMethod())) {
doOptions(request, response, requestContext);
return;
}
// RequestWrapper
request = new RequestWrapper(request, requestContext.getPathParamMap());
if (isMockContext()) {
request.setAttribute(RequestWrapper.class.getName(), request);
}
// ResponseWrapperImpl
response = new ResponseWrapperImpl(response);
// Authorize Origin
String origin = getCrossOrigin(request);
if (origin != null) {
// Not @EnableCors
if (requestContext.getActionDesc().getEnableCors() == null) {
throw new ForbiddenException(requestContext.getResources().get(Resources.ERROR_CORS_NOT_ALLOWED, origin));
}
CorsPolicy corsPolicy = this.corsPolicyProvider.getCorsPolicy(requestContext.getActionDesc().getEnableCors().value());
CorsPolicyHandler.CorsResult corsResult = this.corsPolicyHandler.handleCors(request, response, corsPolicy);
if (corsResult != CorsPolicyHandler.CorsResult.ALLOWED) {
throw new ForbiddenException(requestContext.getResources().get(Resources.ERROR_CORS_NOT_ALLOWED, origin));
}
}
// Authorize
Authorize authorize = requestContext.getActionDesc().getAuthorize();
if (authorize != null) {
UserPrincipal principal = ServletUtils.getUserPrincipal(request);
// Authenticate
if (principal == null) {
this.authHandlerProvider.getAuthHandler(requestContext.getModule()).askAuthenticate(request, response, requestContext);
return;
}
// Check Module
if (!requestContext.getModule().equalsIgnoreCase(principal.getModule())) {
throw new ForbiddenException(Resources.ERROR_FORBIDDEN_MODULE, requestContext.getResources());
}
// REAUTHENTICATE
if (authorize.reauth() && !isReauthenticated(principal)) {
this.authHandlerProvider.getAuthHandler(requestContext.getModule()).askReauthenticate(request, response, requestContext);
return;
}
// Authorize
if (!authorize(request, principal, authorize)) {
throw new ForbiddenException(Resources.ERROR_FORBIDDEN, requestContext.getResources());
}
}
// Invoke filters
chain.doFilter(request, response);
if (((ResponseWrapper) response).getErrorMsg() != null) {
this.appLogger.error(String.format("sendError: status=%d, message=%s", response.getStatus(), ((ResponseWrapper) response).getErrorMsg()));
}
} catch (Throwable ex) {
this.exceptionHandler.handleException(request, response, ex);
}
}
protected boolean authorize(HttpServletRequest request, UserPrincipal principal, Authorize authorize) {
String[] roles = authorize.roles();
String[] policies = authorize.policies();
if ((roles.length == 0) && (policies.length == 0)) {
return true;
}
if (roles.length > 0) {
if (Arrays.stream(roles).anyMatch(role -> request.isUserInRole(role))) {
return true;
}
}
if (policies.length > 0) {
if (this.authorizePolicyProvider.authorize(principal, policies)) {
return true;
}
}
return false;
}
protected boolean isReauthenticated(UserPrincipal principal) {
if (principal.getReauthAt() <= 0) {
return false;
}
final long curTimeMs = System.currentTimeMillis();
if (principal.getReauthAt() >= curTimeMs) {
return false;
}
if ((curTimeMs - principal.getReauthAt()) >= this.appConfig.getReauthTimeoutMs()) {
return false;
}
return true;
}
protected void doOptions(HttpServletRequest request, HttpServletResponse response, RequestContext requestContext) throws Exception {
String origin = request.getHeader(CorsPolicyHandler.HEADER_ORIGIN);
if ((origin == null) || (request.getHeader(CorsPolicyHandler.HEADER_AC_REQUEST_METHOD) == null)) {
throw new ForbiddenException(Resources.ERROR_FORBIDDEN, requestContext.getResources());
}
CorsPolicy corsPolicy = this.corsPolicyProvider.getCorsPolicy(requestContext.getActionDesc().getEnableCors().value());
CorsPolicyHandler.CorsResult corsResult = this.corsPolicyHandler.handlePreflight(request, response, corsPolicy);
if (corsResult != CorsPolicyHandler.CorsResult.ALLOWED) {
throw new ForbiddenException(requestContext.getResources().get(Resources.ERROR_CORS_NOT_ALLOWED, origin));
}
}
public boolean isMockContext() {
return false;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy