Skip to content

Commit

Permalink
Po 884 (#590)
Browse files Browse the repository at this point in the history
* interceptor for incorrect Accept header value

* unit test

* update instanceOf + remove explicit cast
  • Loading branch information
TomReed authored Oct 14, 2024
1 parent aa96a86 commit 8b84f1d
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/main/java/uk/gov/hmcts/opal/annotation/CheckAcceptHeader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package uk.gov.hmcts.opal.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckAcceptHeader {

}
15 changes: 15 additions & 0 deletions src/main/java/uk/gov/hmcts/opal/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package uk.gov.hmcts.opal.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import uk.gov.hmcts.opal.interceptor.AcceptHeaderInterceptor;

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AcceptHeaderInterceptor());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import uk.gov.hmcts.opal.annotation.CheckAcceptHeader;
import uk.gov.hmcts.opal.authorisation.aspect.PermissionNotAllowedException;
import uk.gov.hmcts.opal.authorisation.model.Permissions;
import uk.gov.hmcts.opal.authorisation.model.UserState;
Expand Down Expand Up @@ -138,6 +139,7 @@ public ResponseEntity<List<DraftAccountResponseDto>> postDraftAccountsSearch(

@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Creates a Draft Account Entity in the DB based upon data in request body")
@CheckAcceptHeader
public ResponseEntity<DraftAccountResponseDto> postDraftAccount(@RequestBody AddDraftAccountRequestDto dto,
@RequestHeader(value = "Authorization", required = false) String authHeaderValue) {
log.info(":POST:postDraftAccount: creating a new draft account entity.");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package uk.gov.hmcts.opal.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.method.HandlerMethod;
import uk.gov.hmcts.opal.annotation.CheckAcceptHeader;

public class AcceptHeaderInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler instanceof HandlerMethod handlerMethod
&& handlerMethod.hasMethodAnnotation(CheckAcceptHeader.class)) {
String acceptHeader = request.getHeader("Accept");
if (!isAcceptableMediaType(acceptHeader)) {
response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
response.getWriter().write("{\"error\":\"Not Acceptable\",\"message\""
+ ":\"The requested media type is not supported\"}");
return false;
}
}
return true;
}

private boolean isAcceptableMediaType(String acceptHeader) {
return acceptHeader != null && (acceptHeader.contains("application/json") || acceptHeader.contains("*/*"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package uk.gov.hmcts.opal.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.web.method.HandlerMethod;
import uk.gov.hmcts.opal.annotation.CheckAcceptHeader;

import java.io.PrintWriter;
import java.io.StringWriter;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class AcceptHeaderInterceptorTest {

private AcceptHeaderInterceptor interceptor;

@Mock
private HttpServletRequest request;

@Mock
private HttpServletResponse response;

@Mock
private HandlerMethod handlerMethod;

@BeforeEach
void setUp() {
interceptor = new AcceptHeaderInterceptor();
}

@Test
void preHandle_WithCheckAcceptHeaderAnnotationAndValidJsonAcceptHeader_ShouldReturnTrue() throws Exception {
when(handlerMethod.hasMethodAnnotation(CheckAcceptHeader.class)).thenReturn(true);
when(request.getHeader("Accept")).thenReturn("application/json");

boolean result = interceptor.preHandle(request, response, handlerMethod);

assertTrue(result);
verifyNoInteractions(response);
}

@Test
void preHandle_WithCheckAcceptHeaderAnnotationAndValidWildcardAcceptHeader_ShouldReturnTrue() throws Exception {
when(handlerMethod.hasMethodAnnotation(CheckAcceptHeader.class)).thenReturn(true);
when(request.getHeader("Accept")).thenReturn("*/*");

boolean result = interceptor.preHandle(request, response, handlerMethod);

assertTrue(result);
verifyNoInteractions(response);
}

@Test
void preHandle_WithCheckAcceptHeaderAnnotationAndInvalidAcceptHeader_ShouldReturnFalse() throws Exception {
when(handlerMethod.hasMethodAnnotation(CheckAcceptHeader.class)).thenReturn(true);
when(request.getHeader("Accept")).thenReturn("application/xml");

StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
when(response.getWriter()).thenReturn(writer);

boolean result = interceptor.preHandle(request, response, handlerMethod);

assertFalse(result);
verify(response).setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
writer.flush();
assertTrue(stringWriter.toString().contains("\"error\":\"Not Acceptable\""));
assertTrue(stringWriter.toString().contains("\"message\":\"The requested media type is not supported\""));
}

@Test
void preHandle_WithCheckAcceptHeaderAnnotationAndNullAcceptHeader_ShouldReturnFalse() throws Exception {
when(handlerMethod.hasMethodAnnotation(CheckAcceptHeader.class)).thenReturn(true);
when(request.getHeader("Accept")).thenReturn(null);

StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
when(response.getWriter()).thenReturn(writer);

boolean result = interceptor.preHandle(request, response, handlerMethod);

assertFalse(result);
verify(response).setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
writer.flush();
assertTrue(stringWriter.toString().contains("\"error\":\"Not Acceptable\""));
assertTrue(stringWriter.toString().contains("\"message\":\"The requested media type is not supported\""));
}

@Test
void preHandle_WithoutCheckAcceptHeaderAnnotation_ShouldReturnTrue() throws Exception {
when(handlerMethod.hasMethodAnnotation(CheckAcceptHeader.class)).thenReturn(false);

boolean result = interceptor.preHandle(request, response, handlerMethod);

assertTrue(result);
verifyNoInteractions(response);
}
}

0 comments on commit 8b84f1d

Please sign in to comment.