Skip to content

Commit

Permalink
MLT-0038 Callback for Orders (#39)
Browse files Browse the repository at this point in the history
Simple synq order update endpoint implementation

Includes renaming of restapi module to hostapi module to semantically
separate API for host messages from API for SynQ messages and others as
they come. Making sure that the rest of the naming makes sense the 
product was changed to item, except for SynQ module which uses the word
product.

This includes an attempt to separate SecurityConfig and SwaggerConfig for
these two APIs as well. This works for now but might be problematic if we
use other ways of authentication for SynQ or other systems
  • Loading branch information
MormonJesus69420 authored Oct 8, 2024
1 parent 90db5b8 commit 3d5b335
Show file tree
Hide file tree
Showing 37 changed files with 1,127 additions and 265 deletions.
123 changes: 115 additions & 8 deletions docker/keycloak/import/mlt-local-realm-export.json
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@
"containerId" : "16862c54-915f-451c-bae6-41ea604d20eb",
"attributes" : { }
} ],
"synq" : [ ],
"wls" : [ ]
}
},
Expand Down Expand Up @@ -393,6 +394,20 @@
"realmRoles" : [ "default-roles-mlt-realm" ],
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "1d8f33f8-7381-4a14-9c0f-dd4d9990c12c",
"username" : "service-account-synq",
"emailVerified" : false,
"createdTimestamp" : 1728308503296,
"enabled" : true,
"totp" : false,
"serviceAccountClientId" : "synq",
"credentials" : [ ],
"disableableCredentialTypes" : [ ],
"requiredActions" : [ ],
"realmRoles" : [ "default-roles-mlt-realm" ],
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "410e463f-84ed-4d1b-a8a4-3ddef6fc057d",
"username" : "service-account-wls",
Expand Down Expand Up @@ -528,7 +543,7 @@
"alwaysDisplayInConsole" : false,
"clientAuthenticatorType" : "client-secret",
"secret" : "E93MF2F8UfpRrCowAbVMStvsTzy0gmgr",
"redirectUris" : [ "/*" ],
"redirectUris" : [ "*" ],
"webOrigins" : [ "*" ],
"notBefore" : 0,
"bearerOnly" : false,
Expand Down Expand Up @@ -598,7 +613,7 @@
"jsonType.label" : "String"
}
} ],
"defaultClientScopes" : [ "wls-audience", "wls-order", "wls-subject", "wls-product" ],
"defaultClientScopes" : [ "wls-item", "wls-audience", "wls-order", "wls-subject" ],
"optionalClientScopes" : [ "web-origins", "acr", "address", "phone", "roles", "profile", "offline_access", "microprofile-jwt", "email" ]
}, {
"id" : "2ce493fb-c784-410d-b03d-53d489653ad7",
Expand Down Expand Up @@ -703,6 +718,87 @@
} ],
"defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
"optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
}, {
"id" : "39dcf450-108d-45aa-aec3-c2bfeb34ddb8",
"clientId" : "synq",
"name" : "SynQ (Local Client)",
"description" : "A local development version of the client for SynQ to use in communication with Hermes WLS",
"rootUrl" : "",
"adminUrl" : "",
"baseUrl" : "",
"surrogateAuthRequired" : false,
"enabled" : true,
"alwaysDisplayInConsole" : false,
"clientAuthenticatorType" : "client-secret",
"secret" : "jAiN5r4NjfcXqxpqp48gnqJLw4FFWEXk",
"redirectUris" : [ "*" ],
"webOrigins" : [ "*" ],
"notBefore" : 0,
"bearerOnly" : false,
"consentRequired" : false,
"standardFlowEnabled" : false,
"implicitFlowEnabled" : false,
"directAccessGrantsEnabled" : false,
"serviceAccountsEnabled" : true,
"publicClient" : false,
"frontchannelLogout" : true,
"protocol" : "openid-connect",
"attributes" : {
"oidc.ciba.grant.enabled" : "false",
"client.secret.creation.time" : "1728308503",
"backchannel.logout.session.required" : "true",
"oauth2.device.authorization.grant.enabled" : "false",
"display.on.consent.screen" : "false",
"backchannel.logout.revoke.offline.tokens" : "false"
},
"authenticationFlowBindingOverrides" : { },
"fullScopeAllowed" : true,
"nodeReRegistrationTimeout" : -1,
"protocolMappers" : [ {
"id" : "fb4cfbb5-7db3-4e74-8cd0-98bb0f01039f",
"name" : "Client IP Address",
"protocol" : "openid-connect",
"protocolMapper" : "oidc-usersessionmodel-note-mapper",
"consentRequired" : false,
"config" : {
"user.session.note" : "clientAddress",
"introspection.token.claim" : "true",
"id.token.claim" : "true",
"access.token.claim" : "true",
"claim.name" : "clientAddress",
"jsonType.label" : "String"
}
}, {
"id" : "bfb948ce-99e4-420e-b5c2-b5a42583534b",
"name" : "Client ID",
"protocol" : "openid-connect",
"protocolMapper" : "oidc-usersessionmodel-note-mapper",
"consentRequired" : false,
"config" : {
"user.session.note" : "client_id",
"introspection.token.claim" : "true",
"id.token.claim" : "true",
"access.token.claim" : "true",
"claim.name" : "client_id",
"jsonType.label" : "String"
}
}, {
"id" : "83bf59af-0735-4282-98e3-f790a3f0f1d1",
"name" : "Client Host",
"protocol" : "openid-connect",
"protocolMapper" : "oidc-usersessionmodel-note-mapper",
"consentRequired" : false,
"config" : {
"user.session.note" : "clientHost",
"introspection.token.claim" : "true",
"id.token.claim" : "true",
"access.token.claim" : "true",
"claim.name" : "clientHost",
"jsonType.label" : "String"
}
} ],
"defaultClientScopes" : [ "wls-synq", "wls-item", "wls-audience", "wls-order", "wls-subject" ],
"optionalClientScopes" : [ "web-origins", "acr", "address", "phone", "roles", "profile", "offline_access", "microprofile-jwt", "email" ]
}, {
"id" : "e84865db-9f5c-4213-b9c9-23c911520899",
"clientId" : "wls",
Expand Down Expand Up @@ -793,7 +889,7 @@
"jsonType.label" : "String"
}
} ],
"defaultClientScopes" : [ "wls-audience", "wls-order", "wls-subject", "wls-product" ],
"defaultClientScopes" : [ "wls-item", "wls-audience", "wls-order", "wls-subject" ],
"optionalClientScopes" : [ "web-origins", "acr", "address", "phone", "roles", "profile", "offline_access", "microprofile-jwt", "email" ]
} ],
"clientScopes" : [ {
Expand Down Expand Up @@ -870,8 +966,8 @@
} ]
}, {
"id" : "e77adead-5964-4400-9368-3b1520d57bdd",
"name" : "wls-product",
"description" : "A client scope used to add a 'product' scope to the JWT scope field",
"name" : "wls-item",
"description" : "A client scope used to add a 'item' scope to the JWT scope field",
"protocol" : "openid-connect",
"attributes" : {
"include.in.token.scope" : "true",
Expand Down Expand Up @@ -1185,6 +1281,17 @@
"jsonType.label" : "String"
}
} ]
}, {
"id" : "43e7474f-a3fe-4d44-a0e1-48518c8330f1",
"name" : "wls-synq",
"description" : "A client scope used to add a 'synq' scope to the JWT scope field",
"protocol" : "openid-connect",
"attributes" : {
"include.in.token.scope" : "false",
"display.on.consent.screen" : "false",
"gui.order" : "",
"consent.screen.text" : ""
}
}, {
"id" : "ebcb7370-bd5a-486c-97a8-5147b17a9494",
"name" : "microprofile-jwt",
Expand Down Expand Up @@ -1367,7 +1474,7 @@
"display.on.consent.screen" : "true"
}
} ],
"defaultDefaultClientScopes" : [ "wls-audience", "wls-order", "wls-product", "wls-subject" ],
"defaultDefaultClientScopes" : [ "wls-audience", "wls-order", "wls-subject", "wls-item", "wls-synq" ],
"defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt", "email", "profile", "acr", "role_list", "roles", "web-origins" ],
"browserSecurityHeaders" : {
"contentSecurityPolicyReportOnly" : "",
Expand Down Expand Up @@ -1446,7 +1553,7 @@
"subType" : "authenticated",
"subComponents" : { },
"config" : {
"allowed-protocol-mapper-types" : [ "oidc-usermodel-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper", "oidc-usermodel-property-mapper", "saml-user-attribute-mapper", "oidc-address-mapper", "saml-user-property-mapper", "saml-role-list-mapper" ]
"allowed-protocol-mapper-types" : [ "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-address-mapper", "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper" ]
}
}, {
"id" : "85a712b7-0975-406f-ba2e-c9a99625cfad",
Expand All @@ -1455,7 +1562,7 @@
"subType" : "anonymous",
"subComponents" : { },
"config" : {
"allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper", "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper", "saml-role-list-mapper", "oidc-address-mapper" ]
"allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "saml-user-attribute-mapper", "oidc-address-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper" ]
}
} ],
"org.keycloak.keys.KeyProvider" : [ {
Expand Down
2 changes: 1 addition & 1 deletion docker/mongo/mongo-init.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ db.createCollection('items');

db.items.insertOne({
"hostName": "AXIELL",
"hostId": "product-12345",
"hostId": "item-12345",
"category": "BOOK",
"description": "Tyv etter loven",
"packaging": "NONE",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package no.nb.mlt.wls.application.restapi
package no.nb.mlt.wls.application.hostapi

import no.nb.mlt.wls.domain.ports.inbound.IllegalOrderStateException
import no.nb.mlt.wls.domain.ports.inbound.OrderNotFoundException
Expand Down Expand Up @@ -35,7 +35,7 @@ class ExceptionHandler {
@ExceptionHandler(OrderNotFoundException::class)
fun handleOrderNotFoundException(e: OrderNotFoundException): ResponseEntity<ErrorMessage> {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.status(HttpStatus.NOT_FOUND)
.body(ErrorMessage(e.message))
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package no.nb.mlt.wls.application.restapi.config
package no.nb.mlt.wls.application.hostapi.config

import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.config.web.server.invoke
Expand All @@ -19,7 +21,8 @@ class SecurityConfig {
val issuerUri: String = ""

@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
@Order(Ordered.LOWEST_PRECEDENCE)
fun hostSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
csrf { }
authorizeExchange {
Expand All @@ -30,10 +33,8 @@ class SecurityConfig {
authorize("/webjars/swagger-ui/**", permitAll)
authorize("/actuator", permitAll)
authorize("/actuator/**", permitAll)
authorize("/v1/product", hasAuthority("SCOPE_wls-product"))
authorize("/product", hasAuthority("SCOPE_wls-product"))
authorize("/v1/item", hasAuthority("SCOPE_wls-item"))
authorize("/v1/order", hasAuthority("SCOPE_wls-order"))
authorize("/order", hasAuthority("SCOPE_wls-order"))
authorize(anyExchange, authenticated)
}
oauth2ResourceServer {
Expand All @@ -43,5 +44,6 @@ class SecurityConfig {
}

@Bean
@Order(Ordered.LOWEST_PRECEDENCE)
fun jwtDecoder(): JwtDecoder = JwtDecoders.fromIssuerLocation(issuerUri)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package no.nb.mlt.wls.application.restapi.config
package no.nb.mlt.wls.application.hostapi.config

import io.swagger.v3.oas.annotations.enums.SecuritySchemeType
import io.swagger.v3.oas.annotations.security.OAuthFlow
Expand All @@ -8,8 +8,10 @@ import io.swagger.v3.oas.models.OpenAPI
import io.swagger.v3.oas.models.info.Contact
import io.swagger.v3.oas.models.info.Info
import io.swagger.v3.oas.models.security.SecurityRequirement
import org.springdoc.core.models.GroupedOpenApi
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary

@Configuration
@SecurityScheme(
Expand All @@ -26,6 +28,7 @@ import org.springframework.context.annotation.Configuration
)
class SwaggerConfig {
@Bean
@Primary
fun openApi(): OpenAPI {
return OpenAPI()
.info(
Expand All @@ -52,4 +55,13 @@ class SwaggerConfig {
SecurityRequirement().addList("clientCredentials")
)
}

@Bean
fun hostApi(): GroupedOpenApi {
return GroupedOpenApi.builder()
.group("Host API")
.displayName("Host API for catalogues")
.pathsToMatch("/v1/**")
.build()
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package no.nb.mlt.wls.application.restapi.config
package no.nb.mlt.wls.application.hostapi.config

import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
Expand Down
Loading

0 comments on commit 3d5b335

Please sign in to comment.