Skip to content

Commit

Permalink
conf: make core and authz contexts configurable
Browse files Browse the repository at this point in the history
- mainly to allow using local versions in tests
- also to make easier to use a different version of the core context
  • Loading branch information
bobeal committed Apr 28, 2024
1 parent 3c80bbc commit a37bd58
Show file tree
Hide file tree
Showing 23 changed files with 213 additions and 143 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.egm.stellio.search.web

import arrow.core.raise.either
import com.egm.stellio.search.service.AttributeService
import com.egm.stellio.shared.config.ApplicationProperties
import com.egm.stellio.shared.util.*
import com.egm.stellio.shared.util.JsonLdUtils.expandJsonLdTerm
import org.springframework.http.HttpHeaders
Expand All @@ -13,7 +14,8 @@ import java.util.Optional
@RestController
@RequestMapping("/ngsi-ld/v1/attributes")
class AttributeHandler(
private val attributeService: AttributeService
private val attributeService: AttributeService,
private val applicationProperties: ApplicationProperties
) {
/**
* Implements 6.27 - Retrieve Available Attributes
Expand All @@ -23,7 +25,7 @@ class AttributeHandler(
@RequestHeader httpHeaders: HttpHeaders,
@RequestParam details: Optional<Boolean>
): ResponseEntity<*> = either {
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind()
val mediaType = getApplicableMediaType(httpHeaders).bind()
val detailedRepresentation = details.orElse(false)

Expand All @@ -46,7 +48,7 @@ class AttributeHandler(
@RequestHeader httpHeaders: HttpHeaders,
@PathVariable attrId: String
): ResponseEntity<*> = either {
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind()
val mediaType = getApplicableMediaType(httpHeaders).bind()
val expandedAttribute = expandJsonLdTerm(attrId.decode(), contexts)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class EntityAccessControlHandler(
): ResponseEntity<*> = either {
val sub = getSubFromSecurityContext()

val contexts = getAuthzContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getAuthzContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts).bind()
val mediaType = getApplicableMediaType(httpHeaders).bind()

val entitiesQuery = composeEntitiesQuery(
Expand Down Expand Up @@ -94,7 +94,7 @@ class EntityAccessControlHandler(
): ResponseEntity<*> = either {
val sub = getSubFromSecurityContext()

val contexts = getAuthzContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getAuthzContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts).bind()
val mediaType = getApplicableMediaType(httpHeaders).bind()
val entitiesQuery = composeEntitiesQuery(
applicationProperties.pagination,
Expand Down Expand Up @@ -140,7 +140,7 @@ class EntityAccessControlHandler(

authorizationService.userIsAdmin(sub).bind()

val contexts = getAuthzContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getAuthzContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts).bind()
val mediaType = getApplicableMediaType(httpHeaders).bind()
val entitiesQuery = composeEntitiesQuery(
applicationProperties.pagination,
Expand Down Expand Up @@ -185,7 +185,8 @@ class EntityAccessControlHandler(
val sub = getSubFromSecurityContext()

val body = requestBody.awaitFirst().deserializeAsMap()
val contexts = checkAndGetContext(httpHeaders, body).bind().replaceDefaultContextToAuthzContext()
val contexts = checkAndGetContext(httpHeaders, body, applicationProperties.contexts.core).bind()
.replaceDefaultContextToAuthzContext(applicationProperties.contexts)
val expandedAttributes = expandAttributes(body, contexts)
val ngsiLdAttributes = expandedAttributes.toNgsiLdAttributes().bind()

Expand Down Expand Up @@ -282,7 +283,8 @@ class EntityAccessControlHandler(
authorizationService.userCanAdminEntity(entityId, sub).bind()

val body = requestBody.awaitFirst().deserializeAsMap()
val contexts = checkAndGetContext(httpHeaders, body).bind().replaceDefaultContextToAuthzContext()
val contexts = checkAndGetContext(httpHeaders, body, applicationProperties.contexts.core).bind()
.replaceDefaultContextToAuthzContext(applicationProperties.contexts)
val expandedAttribute = expandAttribute(AUTH_TERM_SAP, body.minus(JSONLD_CONTEXT), contexts)
if (expandedAttribute.first != AUTH_PROP_SAP)
BadRequestDataException("${expandedAttribute.first} is not authorized property name")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ class EntityHandler(
@RequestBody requestBody: Mono<String>
): ResponseEntity<*> = either {
val sub = getSubFromSecurityContext()
val (body, contexts) = extractPayloadAndContexts(requestBody, httpHeaders).bind()
val (body, contexts) =
extractPayloadAndContexts(requestBody, httpHeaders, applicationProperties.contexts.core).bind()
val expandedEntity = expandJsonLdEntity(body, contexts)
val ngsiLdEntity = expandedEntity.toNgsiLdEntity().bind()

Expand Down Expand Up @@ -93,7 +94,8 @@ class EntityHandler(
entityPayloadService.checkEntityExistence(entityId).bind()
authorizationService.userCanUpdateEntity(entityId, sub).bind()

val (body, contexts) = extractPayloadAndContexts(requestBody, httpHeaders).bind()
val (body, contexts) =
extractPayloadAndContexts(requestBody, httpHeaders, applicationProperties.contexts.core).bind()

val observedAt = options.getFirst(QUERY_PARAM_OPTIONS_OBSERVEDAT_VALUE)
?.parseTimeParameter("'observedAt' parameter is not a valid date")
Expand Down Expand Up @@ -141,7 +143,8 @@ class EntityHandler(
@RequestBody requestBody: Mono<String>
): ResponseEntity<*> = either {
val sub = getSubFromSecurityContext()
val (body, contexts) = extractPayloadAndContexts(requestBody, httpHeaders).bind()
val (body, contexts) =
extractPayloadAndContexts(requestBody, httpHeaders, applicationProperties.contexts.core).bind()

val expandedEntity = expandJsonLdEntity(body, contexts)
val ngsiLdEntity = expandedEntity.toNgsiLdEntity().bind()
Expand Down Expand Up @@ -187,7 +190,7 @@ class EntityHandler(
val mediaType = getApplicableMediaType(httpHeaders).bind()
val sub = getSubFromSecurityContext()

val contexts = getContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind()
val entitiesQuery = composeEntitiesQuery(
applicationProperties.pagination,
params,
Expand Down Expand Up @@ -229,7 +232,7 @@ class EntityHandler(
val mediaType = getApplicableMediaType(httpHeaders).bind()
val sub = getSubFromSecurityContext()

val contexts = getContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind()
val queryParams = composeEntitiesQuery(
applicationProperties.pagination,
params,
Expand Down Expand Up @@ -298,7 +301,8 @@ class EntityHandler(

entityPayloadService.checkEntityExistence(entityId).bind()

val (body, contexts) = extractPayloadAndContexts(requestBody, httpHeaders).bind()
val (body, contexts) =
extractPayloadAndContexts(requestBody, httpHeaders, applicationProperties.contexts.core).bind()
val expandedAttributes = expandAttributes(body, contexts)

authorizationService.userCanUpdateEntity(entityId, sub).bind()
Expand Down Expand Up @@ -347,7 +351,8 @@ class EntityHandler(
@RequestBody requestBody: Mono<String>
): ResponseEntity<*> = either {
val sub = getSubFromSecurityContext()
val (body, contexts) = extractPayloadAndContexts(requestBody, httpHeaders).bind()
val (body, contexts) =
extractPayloadAndContexts(requestBody, httpHeaders, applicationProperties.contexts.core).bind()
val expandedAttributes = expandAttributes(body, contexts)

entityPayloadService.checkEntityExistence(entityId).bind()
Expand Down Expand Up @@ -402,7 +407,8 @@ class EntityHandler(
authorizationService.userCanUpdateEntity(entityId, sub).bind()

// We expect an NGSI-LD Attribute Fragment which should be a JSON-LD Object (see 5.4)
val (body, contexts) = extractPayloadAndContexts(requestBody, httpHeaders).bind()
val (body, contexts) =
extractPayloadAndContexts(requestBody, httpHeaders, applicationProperties.contexts.core).bind()

val expandedAttribute = expandAttribute(attrId, body, contexts)

Expand Down Expand Up @@ -450,7 +456,7 @@ class EntityHandler(
val deleteAll = params.getFirst("deleteAll")?.toBoolean() ?: false
val datasetId = params.getFirst("datasetId")?.toUri()

val contexts = getContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind()
val expandedAttrId = expandJsonLdTerm(attrId, contexts)

authorizationService.userCanUpdateEntity(entityId, sub).bind()
Expand Down Expand Up @@ -490,7 +496,8 @@ class EntityHandler(
@RequestBody requestBody: Mono<String>
): ResponseEntity<*> = either {
val sub = getSubFromSecurityContext()
val (body, contexts) = extractPayloadAndContexts(requestBody, httpHeaders).bind()
val (body, contexts) =
extractPayloadAndContexts(requestBody, httpHeaders, applicationProperties.contexts.core).bind()

entityPayloadService.checkEntityExistence(entityId).bind()
authorizationService.userCanUpdateEntity(entityId, sub).bind()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ class EntityOperationHandler(
@RequestParam params: MultiValueMap<String, String>
): ResponseEntity<*> = either {
val sub = getSubFromSecurityContext()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind()
val mediaType = getApplicableMediaType(httpHeaders).bind()
val query = Query(requestBody.awaitFirst()).bind()

Expand Down Expand Up @@ -322,7 +322,10 @@ class EntityOperationHandler(
if (contentType == JSON_LD_MEDIA_TYPE)
expandJsonLdEntityF(it.minus(JSONLD_CONTEXT), it.extractContexts())
else
expandJsonLdEntityF(it, addCoreContextIfMissing(listOfNotNull(context)))
expandJsonLdEntityF(
it,
addCoreContextIfMissing(listOfNotNull(context), applicationProperties.contexts.core)
)
jsonLdExpansionResult
.mapLeft { apiException -> Pair(it[JSONLD_ID_TERM] as String, apiException) }
.flatMap { jsonLdEntity ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.egm.stellio.search.web

import arrow.core.raise.either
import com.egm.stellio.search.service.EntityTypeService
import com.egm.stellio.shared.config.ApplicationProperties
import com.egm.stellio.shared.util.*
import com.egm.stellio.shared.util.JsonLdUtils.expandJsonLdTerm
import org.springframework.http.HttpHeaders
Expand All @@ -13,7 +14,8 @@ import java.util.Optional
@RestController
@RequestMapping("/ngsi-ld/v1/types")
class EntityTypeHandler(
private val entityTypeService: EntityTypeService
private val entityTypeService: EntityTypeService,
private val applicationProperties: ApplicationProperties
) {

/**
Expand All @@ -24,7 +26,7 @@ class EntityTypeHandler(
@RequestHeader httpHeaders: HttpHeaders,
@RequestParam details: Optional<Boolean>
): ResponseEntity<*> = either {
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind()
val mediaType = getApplicableMediaType(httpHeaders).bind()
val detailedRepresentation = details.orElse(false)

Expand All @@ -48,7 +50,7 @@ class EntityTypeHandler(
@RequestHeader httpHeaders: HttpHeaders,
@PathVariable type: String
): ResponseEntity<*> = either {
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind()
val mediaType = getApplicableMediaType(httpHeaders).bind()
val expandedType = expandJsonLdTerm(type.decode(), contexts)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ class TemporalEntityHandler(
@RequestBody requestBody: Mono<String>
): ResponseEntity<*> = either {
val sub = getSubFromSecurityContext()
val (body, contexts) = extractPayloadAndContexts(requestBody, httpHeaders).bind()
val (body, contexts) =
extractPayloadAndContexts(requestBody, httpHeaders, applicationProperties.contexts.core).bind()

val jsonLdTemporalEntity = expandJsonLdEntity(body, contexts)
val entityUri = jsonLdTemporalEntity.id.toUri()
Expand Down Expand Up @@ -110,7 +111,8 @@ class TemporalEntityHandler(
entityPayloadService.checkEntityExistence(entityId).bind()
authorizationService.userCanUpdateEntity(entityId, sub).bind()

val (body, contexts) = extractPayloadAndContexts(requestBody, httpHeaders).bind()
val (body, contexts) =
extractPayloadAndContexts(requestBody, httpHeaders, applicationProperties.contexts.core).bind()
val jsonLdInstances = expandAttributes(body, contexts)
jsonLdInstances.checkTemporalAttributeInstance().bind()
val sortedJsonLdInstances = jsonLdInstances.sorted()
Expand Down Expand Up @@ -140,7 +142,7 @@ class TemporalEntityHandler(
@RequestParam params: MultiValueMap<String, String>
): ResponseEntity<*> = either {
val sub = getSubFromSecurityContext()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind()
val mediaType = getApplicableMediaType(httpHeaders).bind()

val temporalEntitiesQuery =
Expand Down Expand Up @@ -183,7 +185,7 @@ class TemporalEntityHandler(

entityPayloadService.checkEntityExistence(entityId).bind()

val contexts = getContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind()
val mediaType = getApplicableMediaType(httpHeaders).bind()

authorizationService.userCanReadEntity(entityId, sub).bind()
Expand Down Expand Up @@ -223,7 +225,8 @@ class TemporalEntityHandler(
@RequestBody requestBody: Mono<String>
): ResponseEntity<*> = either {
val sub = getSubFromSecurityContext()
val (body, contexts) = extractPayloadAndContexts(requestBody, httpHeaders).bind()
val (body, contexts) =
extractPayloadAndContexts(requestBody, httpHeaders, applicationProperties.contexts.core).bind()
val instanceUri = instanceId.toUri()
attrId.checkNameIsNgsiLdSupported().bind()

Expand Down Expand Up @@ -289,7 +292,7 @@ class TemporalEntityHandler(
val deleteAll = params.getFirst("deleteAll")?.toBoolean() ?: false
val datasetId = params.getFirst("datasetId")?.toUri()

val contexts = getContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind()
attrId.checkNameIsNgsiLdSupported().bind()
val expandedAttrId = expandJsonLdTerm(attrId, contexts)

Expand Down Expand Up @@ -329,7 +332,7 @@ class TemporalEntityHandler(
@PathVariable instanceId: URI
): ResponseEntity<*> = either {
val sub = getSubFromSecurityContext()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind()
attrId.checkNameIsNgsiLdSupported().bind()
val expandedAttrId = expandJsonLdTerm(attrId, contexts)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class TemporalEntityOperationsHandler(
@RequestParam params: MultiValueMap<String, String>
): ResponseEntity<*> = either {
val sub = getSubFromSecurityContext()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders).bind()
val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind()
val mediaType = getApplicableMediaType(httpHeaders).bind()
val query = Query(requestBody.awaitFirst()).bind()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
package com.egm.stellio.search.authorization

import com.egm.stellio.shared.config.ApplicationProperties
import com.egm.stellio.shared.model.BadRequestDataException
import com.egm.stellio.shared.model.toNgsiLdAttribute
import com.egm.stellio.shared.util.*
import com.egm.stellio.shared.util.AuthContextModel.AUTHORIZATION_API_DEFAULT_CONTEXTS
import com.egm.stellio.shared.util.AuthContextModel.AUTH_TERM_SAP
import com.egm.stellio.shared.util.JsonLdUtils.expandAttribute
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test

class AuthUtilsTests {

private val applicationProperties = mockk<ApplicationProperties> {
every {
contexts.defaultAuthzContexts()
} returns listOf(
"http://localhost:8093/jsonld-contexts/authorization.jsonld",
"http://localhost:8093/jsonld-contexts/ngsi-ld-core-context-v1.8.jsonld"
)
}

@Test
fun `it should return a 400 if the payload contains a multi-instance property`() = runTest {
val requestPayload =
Expand All @@ -28,7 +39,7 @@ class AuthUtilsTests {
""".trimIndent()

val ngsiLdAttribute =
expandAttribute(AUTH_TERM_SAP, requestPayload, AUTHORIZATION_API_DEFAULT_CONTEXTS)
expandAttribute(AUTH_TERM_SAP, requestPayload, applicationProperties.contexts.defaultAuthzContexts())
.toNgsiLdAttribute()
.shouldSucceedAndResult()

Expand All @@ -53,7 +64,7 @@ class AuthUtilsTests {
""".trimIndent()

val ngsiLdAttribute =
expandAttribute(AUTH_TERM_SAP, requestPayload, AUTHORIZATION_API_DEFAULT_CONTEXTS)
expandAttribute(AUTH_TERM_SAP, requestPayload, applicationProperties.contexts.defaultAuthzContexts())
.toNgsiLdAttribute()
.shouldSucceedAndResult()

Expand All @@ -72,7 +83,7 @@ class AuthUtilsTests {
""".trimIndent()

val ngsiLdAttribute =
expandAttribute(AUTH_TERM_SAP, requestPayload, AUTHORIZATION_API_DEFAULT_CONTEXTS)
expandAttribute(AUTH_TERM_SAP, requestPayload, applicationProperties.contexts.defaultAuthzContexts())
.toNgsiLdAttribute()
.shouldSucceedAndResult()

Expand All @@ -99,7 +110,7 @@ class AuthUtilsTests {
""".trimIndent()

val ngsiLdAttribute =
expandAttribute(AUTH_TERM_SAP, requestPayload, AUTHORIZATION_API_DEFAULT_CONTEXTS)
expandAttribute(AUTH_TERM_SAP, requestPayload, applicationProperties.contexts.defaultAuthzContexts())
.toNgsiLdAttribute()
.shouldSucceedAndResult()

Expand Down
Loading

0 comments on commit a37bd58

Please sign in to comment.