Unit test for Filter class - java

I am trying to write unit tests for the following Filter class, but I am not sure how it can be done. I am working on a Spring project and trying to fix a vulnerability, that is why I am using this class.
public class HSTSFilter implements Filter {
private static final String HEADER_NAME = "Strict-Transport-Security";
private static final String MAX_AGE_DIRECTIVE = "max-age=%s";
private static final String INCLUDE_SUB_DOMAINS_DIRECTIVE = "includeSubDomains";
private static final Logger logger = LoggerFactory.getLogger(HSTSFilter.class);
private int maxAgeSeconds = 0;
private boolean includeSubDomains = false;
private String directives;
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
logger.info("request.isSecure() :: {}" , request.isSecure());
if (request.isSecure() && response instanceof HttpServletResponse) {
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader(HEADER_NAME, this.directives);
}
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
maxAgeSeconds = Integer.parseInt(filterConfig.getInitParameter("maxAgeSeconds"));
includeSubDomains = "true".equals(filterConfig.getInitParameter("includeSubDomains"));
if (this.maxAgeSeconds <= 0) {
throw new ServletException("Invalid maxAgeSeconds value :: " + maxAgeSeconds);
}
this.directives = String.format(MAX_AGE_DIRECTIVE, this.maxAgeSeconds);
if (this.includeSubDomains) {
this.directives += (" ; " + INCLUDE_SUB_DOMAINS_DIRECTIVE);
}
}
#Override
public void destroy() {
}
}

In your unit test, create a mock object of ServletRequest, ServletResponse, and FilterChain.
Call doFilter with these mock objects.

Related

Request body change when I Use servletRequest.getReader().lines().collect(Collectors.joining())

I'm doing a private api in java, jwt, spring security and the first time come in the request a json object.
user: xxx
password: yyy
The api return a jwt token in the body.
The others call the token come in the body json and to validate it I use this:
sbody = servletRequest.getReader().lines().collect(Collectors.joining());
To get the field token and it get ok, but then of the filter it show the message:
"Required request body is missing: public org.springframework.http.ResponseEntity"
This is my api:
#SpringBootApplication
public class JwtApplication {
public static void main(String[] args) {
SpringApplication.run(JwtApplication.class, args);
}
#EnableWebSecurity
#Configuration
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.addFilterAfter(new JWTAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests().antMatchers(HttpMethod.POST, "/user").permitAll()
.antMatchers(HttpMethod.POST, "/Autenticacion").permitAll().anyRequest().authenticated();
}
}
}
This is the filter:
public class JWTAuthorizationFilter extends OncePerRequestFilter {
private final String HEADER = "Authorization";
private final String SESSION = "sesion";
private final String PREFIX = "Bearer ";
private final String SECRET = "mySecretKey";
public static final long EXPIRATION_TIME = 900_000; // 15 mins
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
try {
boolean resultado_checktoken = checkJWTToken(httpRequest, httpResponse);
if (resultado_checktoken) {
Claims claims = validateToken(request);
if (claims.get("authorities") != null) {
setUpSpringAuthentication(claims);
} else {
SecurityContextHolder.clearContext();
}
} else {
SecurityContextHolder.clearContext();
}
chain.doFilter(request, response);
} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException e) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
return;
}
System.out.println("supuestamente no hubo problemas");
}
private Claims validateToken(HttpServletRequest request) {
//String jwtToken = request.getHeader(HEADER).replace(PREFIX, "");
String jwtToken="";
try {
jwtToken = this.getBodySession(request);
} catch (IOException e) {
e.printStackTrace();
};
return Jwts.parser().setSigningKey(SECRET.getBytes()).parseClaimsJws(jwtToken).getBody();
}
/**
* Authentication method in Spring flow
*
* #param claims
*/
private void setUpSpringAuthentication(Claims claims) {
#SuppressWarnings("unchecked")
List<String> authorities = (List<String>) claims.get("authorities");
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(claims.getSubject(), null,
authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()));
SecurityContextHolder.getContext().setAuthentication(auth);
}
private boolean checkJWTToken(HttpServletRequest request, HttpServletResponse res) throws IOException {
String authenticationHeader = "";
authenticationHeader = this.getBodySession(request);
if (authenticationHeader == null || !authenticationHeader.startsWith(PREFIX))
return false;
return true;
}
public String getBodySession(HttpServletRequest request) throws IOException {
String sbody = "";
HttpServletRequest servletRequest = new ContentCachingRequestWrapper(request);
//servletRequest.getParameterMap();
sbody = servletRequest.getReader().lines().collect(Collectors.joining());
String Field = SESSION;
String scampo = "";
if (sbody.contains(Field)) {
scampo = sbody.substring(sbody.indexOf(Field), sbody.indexOf("\n", sbody.indexOf(Field)))
.replace(Field + "\": \"", "").replace("\"", "").replace(",", "");
}
System.out.println("sbody: " + sbody + " sesion: " + scampo);
return scampo;
}
}
This needs to return a boolean explicitly you cannot have two return statements.
private boolean checkJWTToken(HttpServletRequest request, HttpServletResponse res) throws IOException {
String authenticationHeader = "";
authenticationHeader = this.getBodySession(request);
if (authenticationHeader == null || !authenticationHeader.startsWith(PREFIX))
**return false;**
**return true;**
}

Spring security expression based access control

I'm using Spring Security to protect my resources. I would like to have an expression like following:
http.authorizeRequests().antMatchers("/refugee/{id}").hasRole("BUSINESS");
//Example
http.authorizeRequests().antMatchers("/refugee/{id}").hasRole("REFUGEE").and().access(principal.id==#id);
My SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new TokenAuthenticationFilter(userService), AnonymousAuthenticationFilter.class);
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
//Options preflight
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll();
//ACL
http.authorizeRequests().antMatchers("/auth/**").anonymous();
http.authorizeRequests().antMatchers("/refugee/{id}").hasRole("BUSINESS");
http.authorizeRequests().antMatchers("/refugee/{id}").hasRole("REFUGEE").and();
http.authorizeRequests().antMatchers("/user/**").hasAnyRole("ADMIN", "REFUGEE", "BUSINESS");
}
}
And the TokenAuthenticationFilter:
public class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private UserService userService;
public TokenAuthenticationFilter(UserService userService) {
super("/");
this.userService = userService;
}
private final String HEADER_SECURITY_TOKEN = "Authorization";
private final String PARAMETER_SECURITY_TOKEN = "access_token";
private String token = "";
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
this.token = request.getHeader(HEADER_SECURITY_TOKEN);
if ("".equals(this.token) || this.token == null) {
this.token = request.getParameter(PARAMETER_SECURITY_TOKEN);
}
//Attempt to authenticate
Authentication authResult;
authResult = attemptAuthentication(request, response);
if (authResult == null) {
chain.doFilter(request, response);
} else {
successfulAuthentication(request, response, chain, authResult);
}
}
/**
* Attempt to authenticate request - basically just pass over to another
* method to authenticate request headers
*/
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
Authentication userAuthenticationToken = authUserByToken();
if (userAuthenticationToken == null) {
//throw new AuthenticationServiceException(MessageFormat.format("Error | {0}", "Bad Token"));
}
return userAuthenticationToken;
}
/**
* authenticate the user based on token, mobile app secret & user agent
*
* #return
*/
private Authentication authUserByToken() {
Authentication securityToken = null;
try {
// TODO - just return null - always fail auth just to test spring setup ok
User user = userService.findUserByAccessToken(this.token);
securityToken = new PreAuthenticatedAuthenticationToken(
user, null, user.getAuthorites());
} catch (Exception e) {
logger.error("Authenticate user by token error: ", e);
}
return securityToken;
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
}
}

How to serialize Java object into JSON and return it in servlet filter?

I have this javax.servlet.Filter to check whether client is allowed to access API REST resource.
#Component
public class AuthorizationRequestFilter implements Filter {
public static final String AUTHORIZATION_TOKEN = "X-Access-Token";
#Autowired
#Qualifier("loginService")
private ILoginService loginService;
private void throwUnauthorized(ServletResponse res) throws IOException {
HttpServletResponse response = (HttpServletResponse) res;
response.reset();
response.setHeader("Content-Type", "application/json;charset=UTF-8");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
private void throwForbidden(ServletResponse res) throws IOException {
HttpServletResponse response = (HttpServletResponse) res;
response.reset();
response.setHeader("Content-Type", "application/json;charset=UTF-8");
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
String accessToken = request.getHeader(AUTHORIZATION_TOKEN);
if (StringUtils.isEmpty(accessToken)) {
throwUnauthorized(res);
} else {
AccountLoginData account = loginService.find(accessToken);
if (account == null) {
throwForbidden(res);
}
}
chain.doFilter(req, res);
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
it works but I would like to in these two throw*() methods write to the client JSON with appropriate information. In another part of this application I use these response message objects to inform client what happened.
For example, when record has not been found:
public class NotFoundResponseMessage extends ResponseMessage {
public NotFoundResponseMessage(String message) {
super(HttpStatus.NOT_FOUND, 1, message);
}
}
and
public class ResponseMessage {
private int status;
private int code;
private String message;
private String reason;
public ResponseMessage(int status, int code, String message, String reason) {
Assert.notNull(reason, "Reason must not be null.");
Assert.isTrue(status > 0, "Status must not be empty.");
this.status = status;
this.code = code;
this.message = message;
this.reason = reason;
}
}
My Question
I would like to return JSON with serialized objects (UnauthorizedResponseMessage and ForbiddenResponseMessage) in my javax.servlet.Filter authorization / authentication filter. I use Spring Boot and Jackson library.
How can I manually serialize ResponseMessage into its JSON representation?
How can I write out this JSON back to the client in my filter class?
Edit 1:
private void throwUnauthorized(ServletResponse res) throws IOException {
HttpServletResponse response = (HttpServletResponse) res;
response.reset();
response.setHeader("Content-Type", "application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("{\"foo\":\"boo\"}");
}
Now I can write out JSON but HTTP 500 is returned, because:
java.lang.IllegalStateException: getWriter() has already been called for this response
at org.apache.catalina.connector.Response.getOutputStream(Response.java:544)
Using Jackson convert Object to JSON, the following is an example
ObjectMapper mapper = new ObjectMapper();
String Json = mapper.writeValueAsString(object);
I had the same problem, the complete solution is the following:
try {
restResponse = service.validate(httpReq);
} catch (ForbiddenException e) {
ObjectMapper mapper = new ObjectMapper();
ResponseObject object = new ResponseObject();
object.setStatus(HttpServletResponse.SC_FORBIDDEN);
object.setMessage(e.getMessage());
object.setError("Forbidden");
object.setTimestamp(String.valueOf(new Date().getTime()));
HttpServletResponse httpResp = (HttpServletResponse) response;
httpResp.reset();
httpResp.setHeader("Content-Type","application/json;charset=UTF-8");
httpResp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
String json = mapper.writeValueAsString(object);
response.getWriter().write(json);
return;
}
and the result is:
Just throw your exceptions from the filter and annotate the thrown exception with #ResponseStatus. This way it automatically gets translated to the given http error code. (you can also define the error message)
Code example:
#ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "Error while trying to add the feed.")
public class AddFeedException extends Exception {
private static final long serialVersionUID = 290724913968202592L;
public AddFeedException(Throwable throwable) {
super(throwable);
}
}

#WebFilter exclude url-pattern

I use a filter to check URL patterns for the logged in user.
But I have many URL patterns I need to filter.
{ "/table/*", "/user/*", "/contact/*", "/run/*", "/conf/*", ..., ..., ...}
It's becoming unmaintainable. It will be simpler just to exclude:
{ "/", "/login", "/logout", "/register" }
How can I achieve this?
#WebFilter(urlPatterns = { "/table/*","/user/*", "/contact/*","/run/*","/conf/*"})
public class SessionTimeoutRedirect implements Filter {
protected final Logger logger = LoggerFactory.getLogger("SessionFilter");
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (request.getSession().getAttribute("Id") != null) {
chain.doFilter(req, res);
} else {
logger.debug("session is null:"+request.getRequestURL());
response.sendRedirect(request.getContextPath()+"/login");
}
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
#Override
public void destroy() {
}
}
The servlet API doesn't support an "exclude" URL pattern.
Your best bet is to just map on /* and compare the HttpServletRequest#getRequestURI() against the set of allowed paths.
#WebFilter("/*")
public class LoginFilter implements Filter {
private static final Set<String> ALLOWED_PATHS = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("", "/login", "/logout", "/register")));
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
String path = request.getRequestURI().substring(request.getContextPath().length()).replaceAll("[/]+$", "");
boolean loggedIn = (session != null && session.getAttribute("Id") != null);
boolean allowedPath = ALLOWED_PATHS.contains(path);
if (loggedIn || allowedPath) {
chain.doFilter(req, res);
}
else {
response.sendRedirect(request.getContextPath() + "/login");
}
}
// ...
}
You can use initParam to have some excluded patterns and implement your logic. This is basically the same as BalusC's answer except by using initParam it can be written in the web.xml if you want/need to.
Below I am ignoring some binary (jpeg jpg png pdf) extensions:
#WebFilter(urlPatterns = { "/*" },
initParams = { #WebInitParam(name = "excludedExt", value = "jpeg jpg png pdf") }
)
public class GzipFilter implements Filter {
private static final Set<String> excluded;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
String excludedString = filterConfig.getInitParameter("excludedExt");
if (excludedString != null) {
excluded = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList(excludedString.split(" ", 0))));
} else {
excluded = Collections.<String>emptySet();
}
}
boolean isExcluded(HttpServletRequest request) {
String path = request.getRequestURI();
String extension = path.substring(path.indexOf('.', path.lastIndexOf('/')) + 1).toLowerCase();
return excluded.contains(extension);
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.print("GzipFilter");
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (isExcluded(httpRequest)) {
chain.doFilter(request, response);
return;
}
// Do your stuff here
}
}

How do I write the Java logger Servlet?

I want to do some log in my system, like user action,
and I know in the servelet I can get the request with all the session,parameter..etc
So I want to write the Servlet
public class UserActionCheck extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
Map map = request.getParameterMap();
Set keSet = map.entrySet();
for (Iterator itr = keSet.iterator(); itr.hasNext(); ) {
Map.Entry me = (Map.Entry) itr.next();
Object ok = me.getKey();
Object ov = me.getValue();
String[] value = new String[1];
if (ov instanceof String[]) {
value = (String[]) ov;
} else {
value[0] = ov.toString();
}
for (int k = 0; k < value.length; k++) {
System.out.println(ok + "=" + value[k]);
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//HttpSession session = request.getSession();
}
}
then I can see the parameter output in the tomcat console..but I get the blank page..
It seems the page is stop after doGet method..
so how should I make it continue?
use that RequestDispatcher?
also how to handle in the doPost?
For your purpose, the best way would be to use a Filter.
Example :
#WebFilter(filterName = "monitoringFilter", urlPatterns = { "/*" })
public class MonitoringFilter implements Filter
{
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
{
// Right here do your stuff pretty much like in a servlet
request // ... get information you need
// Process request as normal
chain.doFilter(request,response);
}
#Override
public void init(FilterConfig config) throws ServletException
{
}
#Override
public void destroy()
{
}
}
More info :
Filter
You should use log4j and FileAppender to implement logging in your application.
Something like this :::
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
class A{
static Log log = LogFactory.getLog(A.class);
void methodA(){
try{
log.info("I am inside A");
} catch(Exception e) {
log.error("error" , e);
}
}
}

Categories

Resources