diff --git a/pom.xml b/pom.xml index 160dea2..e0edad8 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 3.1.3 + 3.1.5 @@ -27,7 +27,7 @@ false 1.9.10 - 1.19.0 + 1.19.1 @@ -176,7 +176,7 @@ org.jacoco jacoco-maven-plugin - 0.8.10 + 0.8.11 before-unit-test-execution @@ -234,7 +234,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.1.2 + 3.2.1 false ${surefire.jacoco.args} @@ -248,7 +248,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.1.2 + 3.2.1 ${failsafe.jacoco.args} integration diff --git a/src/main/kotlin/no/digdir/service_catalog/controller/PublicServiceController.kt b/src/main/kotlin/no/digdir/service_catalog/controller/PublicServiceController.kt new file mode 100644 index 0000000..39fda5e --- /dev/null +++ b/src/main/kotlin/no/digdir/service_catalog/controller/PublicServiceController.kt @@ -0,0 +1,28 @@ +package no.digdir.service_catalog.controller + +import no.digdir.service_catalog.model.PublicService +import no.digdir.service_catalog.security.EndpointPermissions +import no.digdir.service_catalog.service.PublicServiceService +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.security.oauth2.jwt.Jwt +import org.springframework.stereotype.Controller +import org.springframework.web.bind.annotation.CrossOrigin +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping + +@Controller +@CrossOrigin +@RequestMapping(value = ["/catalogs/{catalogId}/public-services"]) +class PublicServiceController(private val publicServiceService: PublicServiceService, private val endpointPermissions: EndpointPermissions) { + + @GetMapping + fun getAllPublicServices(@AuthenticationPrincipal jwt: Jwt, @PathVariable catalogId: String): ResponseEntity> = + if (endpointPermissions.hasOrgReadPermission(jwt, catalogId)) { + ResponseEntity(publicServiceService.findPublicServicesByCatalogId(catalogId), HttpStatus.OK) + } else { + ResponseEntity(HttpStatus.FORBIDDEN) + } +} diff --git a/src/main/kotlin/no/digdir/service_catalog/model/LocalizedStrings.kt b/src/main/kotlin/no/digdir/service_catalog/model/LocalizedStrings.kt new file mode 100644 index 0000000..11b1659 --- /dev/null +++ b/src/main/kotlin/no/digdir/service_catalog/model/LocalizedStrings.kt @@ -0,0 +1,12 @@ +package no.digdir.service_catalog.model + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonInclude + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +data class LocalizedStrings( + val nb: String?, + val nn: String?, + val en: String? +) diff --git a/src/main/kotlin/no/digdir/service_catalog/model/PublicService.kt b/src/main/kotlin/no/digdir/service_catalog/model/PublicService.kt new file mode 100644 index 0000000..b155f46 --- /dev/null +++ b/src/main/kotlin/no/digdir/service_catalog/model/PublicService.kt @@ -0,0 +1,18 @@ +package no.digdir.service_catalog.model + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonInclude +import org.springframework.data.mongodb.core.index.CompoundIndex +import org.springframework.data.mongodb.core.index.CompoundIndexes +import org.springframework.data.mongodb.core.mapping.Document + +@Document(collection = "publicServices") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +@CompoundIndexes(value = [CompoundIndex(name = "catalog_id", def = "{'catalogId' : 1}")]) +data class PublicService ( + val id: String, + val catalogId: String, + val title: LocalizedStrings?, + val description: LocalizedStrings? +) diff --git a/src/main/kotlin/no/digdir/service_catalog/mongodb/PublicServiceRepository.kt b/src/main/kotlin/no/digdir/service_catalog/mongodb/PublicServiceRepository.kt new file mode 100644 index 0000000..195e676 --- /dev/null +++ b/src/main/kotlin/no/digdir/service_catalog/mongodb/PublicServiceRepository.kt @@ -0,0 +1,10 @@ +package no.digdir.service_catalog.mongodb + +import no.digdir.service_catalog.model.PublicService +import org.springframework.data.mongodb.repository.MongoRepository +import org.springframework.stereotype.Repository + +@Repository +interface PublicServiceRepository : MongoRepository { + fun getByCatalogId(catalogId: String): List +} diff --git a/src/main/kotlin/no/digdir/service_catalog/service/PublicServiceService.kt b/src/main/kotlin/no/digdir/service_catalog/service/PublicServiceService.kt new file mode 100644 index 0000000..1d6537c --- /dev/null +++ b/src/main/kotlin/no/digdir/service_catalog/service/PublicServiceService.kt @@ -0,0 +1,10 @@ +package no.digdir.service_catalog.service + +import no.digdir.service_catalog.mongodb.PublicServiceRepository +import org.springframework.stereotype.Service + +@Service +class PublicServiceService(private val publicServiceRepository: PublicServiceRepository) { + fun findPublicServicesByCatalogId(catalogId: String) = + publicServiceRepository.getByCatalogId(catalogId) +} diff --git a/src/test/kotlin/no/digdir/service_catalog/integration/PublicServices.kt b/src/test/kotlin/no/digdir/service_catalog/integration/PublicServices.kt new file mode 100644 index 0000000..ae17e30 --- /dev/null +++ b/src/test/kotlin/no/digdir/service_catalog/integration/PublicServices.kt @@ -0,0 +1,48 @@ +package no.digdir.service_catalog.integration + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import no.digdir.service_catalog.model.PublicService +import no.digdir.service_catalog.utils.ApiTestContext +import no.digdir.service_catalog.utils.PUBLIC_SERVICES +import no.digdir.service_catalog.utils.apiAuthorizedRequest +import no.digdir.service_catalog.utils.jwt.Access +import no.digdir.service_catalog.utils.jwt.JwtToken +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Tag +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.HttpStatus +import org.springframework.test.context.ContextConfiguration + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = ["spring.profiles.active=test"]) +@ContextConfiguration(initializers = [ApiTestContext.Initializer::class]) +@Tag("integration") +class PublicServices: ApiTestContext() { + private val mapper = jacksonObjectMapper() + + @Test + fun `able to get all public services`() { + val response = apiAuthorizedRequest("/catalogs/910244132/public-services", port, null, JwtToken(Access.ORG_READ).toString(), "GET") + Assertions.assertEquals(HttpStatus.OK.value(), response["status"]) + + val result: List = mapper.readValue(response["body"] as String) + Assertions.assertEquals(PUBLIC_SERVICES, result) + } + + @Test + fun `unauthorized when missing token`() { + val response = apiAuthorizedRequest("/catalogs/910244132/public-services", port, null, null, "GET") + Assertions.assertEquals(HttpStatus.UNAUTHORIZED.value(), response["status"]) + } + + @Test + fun `forbidden when authorized for other catalog`() { + val response = apiAuthorizedRequest("/catalogs/910244132/public-services", port, null, JwtToken(Access.WRONG_ORG_READ).toString(), "GET") + Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response["status"]) + } +} diff --git a/src/test/kotlin/no/digdir/service_catalog/utils/ApiTestContext.kt b/src/test/kotlin/no/digdir/service_catalog/utils/ApiTestContext.kt index 4278025..85aef06 100644 --- a/src/test/kotlin/no/digdir/service_catalog/utils/ApiTestContext.kt +++ b/src/test/kotlin/no/digdir/service_catalog/utils/ApiTestContext.kt @@ -1,5 +1,6 @@ package no.digdir.service_catalog.utils +import no.digdir.service_catalog.mongodb.PublicServiceRepository import no.digdir.service_catalog.mongodb.ServiceRepository import org.junit.jupiter.api.BeforeEach import org.springframework.beans.factory.annotation.Autowired @@ -18,10 +19,17 @@ abstract class ApiTestContext { @Autowired private lateinit var serviceRepository: ServiceRepository + + @Autowired + private lateinit var publicServiceRepository: PublicServiceRepository + @BeforeEach fun resetDatabase() { serviceRepository.deleteAll() serviceRepository.saveAll(SERVICES) + + publicServiceRepository.deleteAll() + publicServiceRepository.saveAll(PUBLIC_SERVICES) } internal class Initializer : ApplicationContextInitializer { diff --git a/src/test/kotlin/no/digdir/service_catalog/utils/TestData.kt b/src/test/kotlin/no/digdir/service_catalog/utils/TestData.kt index 1faa7d6..f0446e8 100644 --- a/src/test/kotlin/no/digdir/service_catalog/utils/TestData.kt +++ b/src/test/kotlin/no/digdir/service_catalog/utils/TestData.kt @@ -1,5 +1,7 @@ package no.digdir.service_catalog.utils +import no.digdir.service_catalog.model.LocalizedStrings +import no.digdir.service_catalog.model.PublicService import no.digdir.service_catalog.model.Service import org.testcontainers.shaded.com.google.common.collect.ImmutableMap @@ -17,3 +19,14 @@ val SERVICE_0 = Service("0", "title 0") val SERVICE_1 = Service("1", "title 1") val SERVICES = listOf(SERVICE_0, SERVICE_1) + +val PUBLIC_SERVICE_0 = + PublicService("0", "910244132", + title = LocalizedStrings("Tittel 0", "Tittel 0", "Tittel 0"), + description = LocalizedStrings("Beskrivelse 0", "Beskriving 0", "Description 0")) +val PUBLIC_SERVICE_1 = + PublicService("1", "910244132", + title = LocalizedStrings("Tittel 1", "Tittel 1", "Tittel 1"), + description = LocalizedStrings("Beskrivelse 1", "Beskriving 1", "Description 1")) + +val PUBLIC_SERVICES = listOf(PUBLIC_SERVICE_0, PUBLIC_SERVICE_1)