diff --git a/README.md b/README.md index df2e8c8..f3da862 100644 --- a/README.md +++ b/README.md @@ -1 +1,8 @@ -# laa-ccms-caab-assessment-api \ No newline at end of file +# laa-ccms-caab-assessment-api + +## Common Components + +This API uses components from the [LAA CCMS Common Library](https://github.com/ministryofjustice/laa-ccms-spring-boot-common): + +- [laa-ccms-spring-boot-plugin](https://github.com/ministryofjustice/laa-ccms-spring-boot-common?tab=readme-ov-file#laa-ccms-spring-boot-gradle-plugin-for-java--spring-boot-projects) +- [laa-ccms-spring-boot-starter-auth](https://github.com/ministryofjustice/laa-ccms-spring-boot-common/tree/main/laa-ccms-spring-boot-starters/laa-ccms-spring-boot-starter-auth) diff --git a/assessment-api/build.gradle b/assessment-api/build.gradle index b3adef3..82620a8 100644 --- a/assessment-api/build.gradle +++ b/assessment-api/build.gradle @@ -6,9 +6,9 @@ apply plugin: 'uk.gov.laa.ccms.springboot.laa-ccms-spring-boot-gradle-plugin' dependencies { + implementation 'io.swagger.core.v3:swagger-annotations:2.2.22' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.data:spring-data-commons' - implementation 'org.springdoc:springdoc-openapi-ui:1.7.0' implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' @@ -39,13 +39,14 @@ openApiGenerate { configOptions = [ delegatePattern : "false", interfaceOnly : "true", // This will only generate interfaces, not implementations - dateLibrary : "java17", - java17 : "true", + dateLibrary : "legacy", useTags : "true", skipDefaultInterface : "true", useJakartaEe : "true", documentationProvider : "none", - serializableModel : "true" + serializableModel : "true", + annotationLibrary : "swagger2", + useSpringBoot3 : "true" ] } diff --git a/assessment-api/open-api-specification.yml b/assessment-api/open-api-specification.yml index cd1002e..2551bcd 100644 --- a/assessment-api/open-api-specification.yml +++ b/assessment-api/open-api-specification.yml @@ -25,6 +25,8 @@ paths: description: 'Bad request' '401': description: 'Unauthorized' + '403': + description: 'Forbidden' '404': description: 'Not found' '500': @@ -57,6 +59,8 @@ paths: description: 'Bad request' '401': description: 'Unauthorized' + '403': + description: 'Forbidden' '404': description: 'Not found' '500': @@ -81,6 +85,8 @@ paths: description: 'Bad request' '401': description: 'Unauthorized' + '403': + description: 'Forbidden' '500': description: 'Internal server error' /assessments/{assessment-id}: @@ -102,6 +108,8 @@ paths: description: 'Bad request' '401': description: 'Unauthorized' + '403': + description: 'Forbidden' '404': description: 'Not found' '500': @@ -127,6 +135,8 @@ paths: description: 'Bad request' '401': description: 'Unauthorized' + '403': + description: 'Forbidden' '500': description: 'Internal server error' patch: @@ -150,6 +160,8 @@ paths: description: 'Bad request' '401': description: 'Unauthorized' + '403': + description: 'Forbidden' '404': description: 'Not found' '500': @@ -170,12 +182,19 @@ paths: description: 'Bad request' '401': description: 'Unauthorized' + '403': + description: 'Forbidden' '404': description: 'Not found' '500': description: 'Internal server error' components: + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: Authorization parameters: assessmentType: name: name @@ -372,3 +391,5 @@ components: target_entity_id: type: string +security: + - ApiKeyAuth: [] diff --git a/assessment-service/build.gradle b/assessment-service/build.gradle index 4e867dd..ae83718 100644 --- a/assessment-service/build.gradle +++ b/assessment-service/build.gradle @@ -7,8 +7,11 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation files('lib/ojdbc8.jar') + //Enable access token authentication + implementation 'uk.gov.laa.ccms.springboot:laa-ccms-spring-boot-starter-auth' + //Enable Swagger UI - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' diff --git a/assessment-service/src/integrationTest/resources/application-local.yml b/assessment-service/src/integrationTest/resources/application-local.yml index b931eee..653e874 100644 --- a/assessment-service/src/integrationTest/resources/application-local.yml +++ b/assessment-service/src/integrationTest/resources/application-local.yml @@ -8,4 +8,4 @@ spring: jpa: database-platform: org.hibernate.dialect.OracleDialect hibernate: - ddl-auto: none \ No newline at end of file + ddl-auto: none diff --git a/assessment-service/src/integrationTest/resources/application.yml b/assessment-service/src/integrationTest/resources/application.yml new file mode 100644 index 0000000..9da12a0 --- /dev/null +++ b/assessment-service/src/integrationTest/resources/application.yml @@ -0,0 +1,20 @@ +laa.ccms.springboot.starter.auth: + authentication-header: "Authorization" + authorized-clients: '[ + { + "name": "integration-test-runner", + "roles": [ + "ALL" + ], + "token": "78bd752c-814c-4fb5-801b-193839c8e768" + } + ]' + authorized-roles: '[ + { + "name": "ALL", + "URIs": [ + "/**" + ] + } + ]' + unprotected-uris: [ "/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**", "/favicon.ico", "/open-api-specification.yml"] diff --git a/assessment-service/src/main/resources/application-local.yml b/assessment-service/src/main/resources/application-local.yml index 76acc86..c42e219 100644 --- a/assessment-service/src/main/resources/application-local.yml +++ b/assessment-service/src/main/resources/application-local.yml @@ -12,3 +12,25 @@ spring: server: port: 8008 + + +laa.ccms.springboot.starter.auth: + authentication-header: "Authorization" + authorized-clients: '[ + { + "name": "caab-ui", + "roles": [ + "ALL" + ], + "token": "78bd752c-814c-4fb5-801b-193839c8e768" + } + ]' + authorized-roles: '[ + { + "name": "ALL", + "URIs": [ + "/**" + ] + } + ]' + unprotected-uris: [ "/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**", "/favicon.ico", "/open-api-specification.yml"] \ No newline at end of file diff --git a/assessment-service/src/main/resources/application.yml b/assessment-service/src/main/resources/application.yml index cf2f0b1..7058847 100644 --- a/assessment-service/src/main/resources/application.yml +++ b/assessment-service/src/main/resources/application.yml @@ -10,3 +10,9 @@ spring: hibernate: ddl-auto: none open-in-view: false + +laa.ccms.springboot.starter.auth: + authentication-header: "Authorization" + authorized-clients: ${AUTHORIZED_CLIENTS} + authorized-roles: ${AUTHORIZED_ROLES} + unprotected-uris: ${UNPROTECTED_URIS} diff --git a/assessment-service/src/test/java/uk/gov/laa/ccms/caab/assessment/controller/AssessmentControllerTest.java b/assessment-service/src/test/java/uk/gov/laa/ccms/caab/assessment/controller/AssessmentControllerTest.java index c9c7832..201c4b5 100644 --- a/assessment-service/src/test/java/uk/gov/laa/ccms/caab/assessment/controller/AssessmentControllerTest.java +++ b/assessment-service/src/test/java/uk/gov/laa/ccms/caab/assessment/controller/AssessmentControllerTest.java @@ -1,5 +1,9 @@ package uk.gov.laa.ccms.caab.assessment.controller; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; @@ -8,37 +12,48 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.ArrayList; -import java.util.List; +import jakarta.servlet.ServletException; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; -import uk.gov.laa.ccms.caab.assessment.advice.GlobalExceptionHandler; import uk.gov.laa.ccms.caab.assessment.exception.ApplicationException; import uk.gov.laa.ccms.caab.assessment.model.AssessmentDetail; import uk.gov.laa.ccms.caab.assessment.model.AssessmentDetails; import uk.gov.laa.ccms.caab.assessment.model.PatchAssessmentDetail; import uk.gov.laa.ccms.caab.assessment.service.AssessmentService; -@WebMvcTest(AssessmentController.class) -@Import(GlobalExceptionHandler.class) +import java.util.ArrayList; +import java.util.List; + +@ExtendWith(SpringExtension.class) +@WebAppConfiguration class AssessmentControllerTest { - @MockBean + @Mock private AssessmentService assessmentService; - @Autowired + @InjectMocks + private AssessmentController applicationController; + private MockMvc mockMvc; + @BeforeEach + public void setup() { + mockMvc = standaloneSetup(applicationController) + .build(); + } + @Test public void createAssessment_createsAssessmentSuccessfully() throws Exception { Long assessmentId = 1L; @@ -71,7 +86,7 @@ public void getAssessment() throws Exception { } @Test - public void getAssessment_throwsNotFound() throws Exception { + public void getAssessment_throwsNotFound() { Long assessmentId = 1L; String errorMessage = String.format("Assessment with id %s not found", assessmentId); @@ -81,10 +96,16 @@ public void getAssessment_throwsNotFound() throws Exception { errorMessage, HttpStatus.NOT_FOUND)); - this.mockMvc.perform(get("/assessments/{assessment-id}", assessmentId)) - .andExpect(status().isNotFound()) - .andExpect(jsonPath("$.error_message").value(errorMessage)) - .andExpect(jsonPath("$.http_status").value(HttpStatus.NOT_FOUND.value())); + ServletException ex = assertThrows(ServletException.class, () -> + this.mockMvc.perform(get("/assessments/{assessment-id}", assessmentId)), + "Expected ServletException to be thrown, but wasn't."); + + assertTrue(ex.getMessage().contains(errorMessage)); + assertInstanceOf(ApplicationException.class, ex.getRootCause()); + + ApplicationException appEx = (ApplicationException) ex.getRootCause(); + assertEquals(HttpStatus.NOT_FOUND, appEx.getHttpStatus()); + assertEquals(errorMessage, appEx.getErrorMessage()); verify(assessmentService).getAssessment(assessmentId); } diff --git a/assessment-service/src/test/resources/application.yml b/assessment-service/src/test/resources/application.yml index fd9781d..186d7ae 100644 --- a/assessment-service/src/test/resources/application.yml +++ b/assessment-service/src/test/resources/application.yml @@ -5,4 +5,25 @@ spring: jpa: database-platform: org.hibernate.dialect.H2Dialect hibernate: - ddl-auto: none \ No newline at end of file + ddl-auto: none + +laa.ccms.springboot.starter.auth: + authentication-header: "Authorization" + authorized-clients: '[ + { + "name": "test-runner", + "roles": [ + "ALL" + ], + "token": "78bd752c-814c-4fb5-801b-193839c8e768" + } + ]' + authorized-roles: '[ + { + "name": "ALL", + "URIs": [ + "/**" + ] + } + ]' + unprotected-uris: [ "" ] \ No newline at end of file diff --git a/build.gradle b/build.gradle index 4844488..7f3d1c2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'net.researchgate.release' version '3.0.2' - id 'uk.gov.laa.ccms.springboot.laa-ccms-spring-boot-gradle-plugin' version '0.0.2' apply false + id 'uk.gov.laa.ccms.springboot.laa-ccms-spring-boot-gradle-plugin' version '0.0.3' apply false } subprojects {