diff --git a/.config/owasp-suppressions.xml b/.config/owasp-suppressions.xml index 4f117ce003..d77ddac247 100644 --- a/.config/owasp-suppressions.xml +++ b/.config/owasp-suppressions.xml @@ -1,26 +1,5 @@ - - - org\.jetbrains\.kotlin:.* - CVE-2022-24329 - - - - com\.google\.guava:guava.* - CVE-2020-8908 - - - - com\.google\.guava:guava.* - CVE-2023-2976 - org\.eclipse\.jetty\.toolchain:jetty\-jakarta\-websocket\-api.* .* + + + ^pkg:maven/org\.graalvm\.sdk/graal\-sdk@.*$ + CVE-2023-22006 + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c98a85fac0..4e640a0031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- Introduced new API endpoint to register ESS Jobs in Batch - POST {{IRS_HOST}}/irs/ess/orders + +### Changed +- Deprecated query parameter 'jobStates' was removed from GET {{IRS_HOST}}/irs/jobs endpoint +- Moved OAuth2 JWT token claim to configuration. The fields can be configured with `oauth.resourceClaim`, `oauth.irsNamespace`, `oauth.roles`. ## [3.5.3] - 2023-10-09 ### Fixed diff --git a/DEPENDENCIES b/DEPENDENCIES index 097c2cc537..23338023a7 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -120,9 +120,9 @@ maven/mavencentral/io.github.resilience4j/resilience4j-retry/2.1.0, Apache-2.0, maven/mavencentral/io.github.resilience4j/resilience4j-spring-boot3/2.1.0, Apache-2.0, approved, #10913 maven/mavencentral/io.github.resilience4j/resilience4j-spring6/2.1.0, Apache-2.0, approved, #10915 maven/mavencentral/io.github.resilience4j/resilience4j-timelimiter/2.1.0, Apache-2.0, approved, #10166 -maven/mavencentral/io.micrometer/micrometer-commons/1.11.3, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9243 -maven/mavencentral/io.micrometer/micrometer-core/1.11.3, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9238 -maven/mavencentral/io.micrometer/micrometer-observation/1.11.3, Apache-2.0, approved, #9242 +maven/mavencentral/io.micrometer/micrometer-commons/1.11.4, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9243 +maven/mavencentral/io.micrometer/micrometer-core/1.11.4, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9238 +maven/mavencentral/io.micrometer/micrometer-observation/1.11.4, Apache-2.0, approved, #9242 maven/mavencentral/io.micrometer/micrometer-registry-prometheus/1.11.4, Apache-2.0, approved, #9805 maven/mavencentral/io.minio/minio/8.5.6, Apache-2.0, approved, #9097 maven/mavencentral/io.netty.incubator/netty-incubator-transport-classes-io_uring/0.0.21.Final, Apache-2.0, approved, #9622 @@ -158,18 +158,17 @@ maven/mavencentral/io.prometheus/simpleclient_tracer_common/0.16.0, Apache-2.0, maven/mavencentral/io.prometheus/simpleclient_tracer_otel/0.16.0, Apache-2.0, approved, clearlydefined maven/mavencentral/io.prometheus/simpleclient_tracer_otel_agent/0.16.0, Apache-2.0, approved, clearlydefined maven/mavencentral/io.rest-assured/json-path/5.3.0, Apache-2.0, approved, #9261 -maven/mavencentral/io.rest-assured/json-path/5.3.1, Apache-2.0, approved, #9261 +maven/mavencentral/io.rest-assured/json-path/5.3.2, Apache-2.0, approved, #9261 maven/mavencentral/io.rest-assured/rest-assured-common/5.3.0, Apache-2.0, approved, #9264 -maven/mavencentral/io.rest-assured/rest-assured-common/5.3.1, Apache-2.0, approved, #9264 +maven/mavencentral/io.rest-assured/rest-assured-common/5.3.2, Apache-2.0, approved, #9264 maven/mavencentral/io.rest-assured/rest-assured/5.3.0, Apache-2.0, approved, #9262 maven/mavencentral/io.rest-assured/xml-path/5.3.0, Apache-2.0, approved, #9267 -maven/mavencentral/io.rest-assured/xml-path/5.3.1, Apache-2.0, approved, #9267 +maven/mavencentral/io.rest-assured/xml-path/5.3.2, Apache-2.0, approved, #9267 maven/mavencentral/io.suzaku/boopickle_2.13/1.3.3, Apache-2.0, approved, clearlydefined maven/mavencentral/io.swagger.core.v3/swagger-annotations-jakarta/2.2.15, Apache-2.0, approved, #5947 maven/mavencentral/io.swagger.core.v3/swagger-annotations/2.2.16, Apache-2.0, approved, clearlydefined maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.15, Apache-2.0, approved, #5929 maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.15, Apache-2.0, approved, #5919 -maven/mavencentral/jakarta.activation/jakarta.activation-api/2.1.0, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf maven/mavencentral/jakarta.activation/jakarta.activation-api/2.1.2, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf maven/mavencentral/jakarta.annotation/jakarta.annotation-api/2.1.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.ca maven/mavencentral/jakarta.inject/jakarta.inject-api/2.0.1, Apache-2.0, approved, clearlydefined @@ -179,14 +178,14 @@ maven/mavencentral/jakarta.transaction/jakarta.transaction-api/2.0.0, EPL-2.0 OR maven/mavencentral/jakarta.transaction/jakarta.transaction-api/2.0.1, EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, #7697 maven/mavencentral/jakarta.validation/jakarta.validation-api/3.0.2, Apache-2.0, approved, ee4j.validation maven/mavencentral/jakarta.ws.rs/jakarta.ws.rs-api/3.1.0, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.rest -maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/4.0.0, BSD-3-Clause, approved, ee4j.jaxb +maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/4.0.1, BSD-3-Clause, approved, ee4j.jaxb maven/mavencentral/javax.jms/javax.jms-api/2.0.1, CDDL-1.1 OR GPL-2.0 WITH Classpath-exception-2.0, approved, #1516 maven/mavencentral/joda-time/joda-time/2.10.2, Apache-2.0, approved, clearlydefined maven/mavencentral/junit/junit/4.13.2, EPL-2.0, approved, CQ23636 maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.14.4, Apache-2.0, approved, #7164 -maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.14.6, Apache-2.0, approved, #7164 +maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.14.8, Apache-2.0, approved, #7164 maven/mavencentral/net.bytebuddy/byte-buddy/1.12.21, Apache-2.0 AND BSD-3-Clause, approved, #1811 -maven/mavencentral/net.bytebuddy/byte-buddy/1.14.6, Apache-2.0 AND BSD-3-Clause, approved, #7163 +maven/mavencentral/net.bytebuddy/byte-buddy/1.14.8, Apache-2.0 AND BSD-3-Clause, approved, #7163 maven/mavencentral/net.datafaker/datafaker/1.9.0, Apache-2.0, approved, #8797 maven/mavencentral/net.debasishg/redisclient_2.13/3.42, Apache-2.0, approved, clearlydefined maven/mavencentral/net.java.dev.jna/jna/5.12.1, Apache-2.0 OR LGPL-2.1-or-later, approved, #3217 @@ -206,9 +205,9 @@ maven/mavencentral/org.apache.commons/commons-compress/1.23.0, Apache-2.0 AND BS maven/mavencentral/org.apache.commons/commons-compress/1.24.0, Apache-2.0 AND BSD-3-Clause AND bzip2-1.0.6 AND LicenseRef-Public-Domain, approved, #10368 maven/mavencentral/org.apache.commons/commons-lang3/3.12.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.commons/commons-pool2/2.11.1, Apache-2.0, approved, CQ23795 -maven/mavencentral/org.apache.groovy/groovy-json/4.0.14, Apache-2.0, approved, #7411 -maven/mavencentral/org.apache.groovy/groovy-xml/4.0.14, Apache-2.0, approved, #10179 -maven/mavencentral/org.apache.groovy/groovy/4.0.14, Apache-2.0 AND BSD-3-Clause AND MIT, approved, #1742 +maven/mavencentral/org.apache.groovy/groovy-json/4.0.15, Apache-2.0, approved, #7411 +maven/mavencentral/org.apache.groovy/groovy-xml/4.0.15, Apache-2.0, approved, #10179 +maven/mavencentral/org.apache.groovy/groovy/4.0.15, Apache-2.0 AND BSD-3-Clause AND MIT, approved, #1742 maven/mavencentral/org.apache.httpcomponents/httpclient/4.5.13, Apache-2.0 AND LicenseRef-Public-Domain, approved, CQ23527 maven/mavencentral/org.apache.httpcomponents/httpcore/4.4.16, Apache-2.0, approved, CQ23528 maven/mavencentral/org.apache.httpcomponents/httpmime/4.5.13, Apache-2.0, approved, CQ11718 @@ -217,9 +216,9 @@ maven/mavencentral/org.apache.logging.log4j/log4j-core/2.20.0, Apache-2.0 AND (A maven/mavencentral/org.apache.logging.log4j/log4j-jul/2.20.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.logging.log4j/log4j-slf4j2-impl/2.20.0, Apache-2.0, approved, #8801 maven/mavencentral/org.apache.logging.log4j/log4j-to-slf4j/2.20.0, Apache-2.0, approved, #8799 -maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-core/10.1.12, Apache-2.0 AND (EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0) AND (CDDL-1.0 OR GPL-2.0-only WITH Classpath-exception-2.0) AND W3C AND CC0-1.0, approved, #5949 -maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-el/10.1.12, Apache-2.0, approved, #6997 -maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.12, Apache-2.0, approved, #7920 +maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-core/10.1.13, Apache-2.0 AND (EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0) AND (CDDL-1.0 OR GPL-2.0-only WITH Classpath-exception-2.0) AND W3C AND CC0-1.0, approved, #5949 +maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-el/10.1.13, Apache-2.0, approved, #6997 +maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.13, Apache-2.0, approved, #7920 maven/mavencentral/org.apiguardian/apiguardian-api/1.1.2, Apache-2.0, approved, clearlydefined maven/mavencentral/org.aspectj/aspectjweaver/1.9.20, EPL-1.0, approved, tools.aspectj maven/mavencentral/org.assertj/assertj-core/3.24.2, Apache-2.0, approved, #6161 @@ -279,29 +278,25 @@ maven/mavencentral/org.eclipse.edc/validator-spi/0.1.3, Apache-2.0, approved, te maven/mavencentral/org.eclipse.edc/web-spi/0.1.3, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.jetty.toolchain/jetty-jakarta-servlet-api/5.0.2, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty.toolchain/jetty-jakarta-websocket-api/2.0.0, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-client/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-common/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-server/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-client/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-common/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-server/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty.websocket/websocket-servlet/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-alpn-client/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-annotations/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-client/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-http/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-client/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-common/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-server/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-client/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-common/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-server/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-servlet/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-alpn-client/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-annotations/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-client/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty/jetty-http/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-io/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty/jetty-io/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-jndi/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-plus/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-security/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-server/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-servlet/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-util/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-jndi/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-plus/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-security/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-server/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-servlet/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty/jetty-util/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-webapp/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-xml/11.0.15, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-webapp/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty/jetty-xml/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.tractusx.irs/irs-api/0.0.2-SNAPSHOT, Apache-2.0, approved, automotive.tractusx maven/mavencentral/org.eclipse.tractusx.irs/irs-common/0.0.2-SNAPSHOT, Apache-2.0, approved, automotive.tractusx @@ -335,12 +330,8 @@ maven/mavencentral/org.glassfish.jersey.media/jersey-media-multipart/3.1.2, EPL- maven/mavencentral/org.glassfish.jersey.media/jersey-media-multipart/3.1.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey maven/mavencentral/org.glassfish/jakarta.json/2.0.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jsonp maven/mavencentral/org.graalvm.js/js/21.2.0, UPL-1.0 AND (MPL-2.0 AND LicenseRef-MIT-style) AND (BSD-3-Clause AND UPL-1.0) AND (GPL-2.0-only WITH Classpath-exception-2.0 AND UPL-1.0) AND (UPL-1.0 AND LicenseRef-Permission-Notice), approved, #10176 -maven/mavencentral/org.graalvm.polyglot/polyglot/23.1.0, UPL-1.0, approved, #10918 maven/mavencentral/org.graalvm.regex/regex/21.2.0, UPL-1.0 AND (Unicode-TOU AND UPL-1.0), approved, #10181 -maven/mavencentral/org.graalvm.sdk/collections/23.1.0, UPL-1.0, approved, #10920 -maven/mavencentral/org.graalvm.sdk/graal-sdk/23.1.0, UPL-1.0, approved, #10914 -maven/mavencentral/org.graalvm.sdk/nativeimage/23.1.0, UPL-1.0, approved, #10921 -maven/mavencentral/org.graalvm.sdk/word/23.1.0, UPL-1.0, approved, #10917 +maven/mavencentral/org.graalvm.sdk/graal-sdk/21.2.0, UPL-1.0, approved, clearlydefined maven/mavencentral/org.graalvm.truffle/truffle-api/21.2.0, UPL-1.0, approved, #10219 maven/mavencentral/org.hamcrest/hamcrest-core/2.2, BSD-3-Clause, approved, clearlydefined maven/mavencentral/org.hamcrest/hamcrest/2.2, BSD-3-Clause, approved, clearlydefined @@ -392,7 +383,8 @@ maven/mavencentral/org.opentest4j/opentest4j/1.2.0, Apache-2.0, approved, clearl maven/mavencentral/org.ow2.asm/asm-commons/9.5, BSD-3-Clause, approved, #7553 maven/mavencentral/org.ow2.asm/asm-tree/9.5, BSD-3-Clause, approved, #7555 maven/mavencentral/org.ow2.asm/asm/9.3, BSD-3-Clause, approved, clearlydefined -maven/mavencentral/org.projectlombok/lombok/1.18.28, MIT AND LicenseRef-Public-Domain, approved, CQ23907 +maven/mavencentral/org.ow2.asm/asm/9.5, BSD-3-Clause, approved, #7554 +maven/mavencentral/org.projectlombok/lombok/1.18.30, MIT AND LicenseRef-Public-Domain, approved, CQ23907 maven/mavencentral/org.rnorth.duct-tape/duct-tape/1.0.8, MIT, approved, clearlydefined maven/mavencentral/org.scala-lang.modules/scala-java8-compat_2.13/1.0.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.scala-lang.modules/scala-parser-combinators_2.13/2.3.0, Apache-2.0, approved, #8628 @@ -401,51 +393,51 @@ maven/mavencentral/org.scala-lang/scala-reflect/2.13.10, Apache-2.0, approved, # maven/mavencentral/org.simpleflatmapper/lightning-csv/8.2.3, MIT, approved, clearlydefined maven/mavencentral/org.simpleflatmapper/sfm-util/8.2.3, MIT, approved, clearlydefined maven/mavencentral/org.skyscreamer/jsonassert/1.5.1, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.slf4j/jul-to-slf4j/2.0.7, MIT, approved, #7698 -maven/mavencentral/org.slf4j/slf4j-api/2.0.7, MIT, approved, #5915 +maven/mavencentral/org.slf4j/jul-to-slf4j/2.0.9, MIT, approved, #7698 +maven/mavencentral/org.slf4j/slf4j-api/2.0.9, MIT, approved, #5915 maven/mavencentral/org.springdoc/springdoc-openapi-starter-common/2.2.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.springdoc/springdoc-openapi-starter-webmvc-api/2.2.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.springdoc/springdoc-openapi-starter-webmvc-ui/2.2.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.springframework.boot/spring-boot-actuator-autoconfigure/3.1.3, Apache-2.0, approved, #9348 -maven/mavencentral/org.springframework.boot/spring-boot-actuator/3.1.3, Apache-2.0, approved, #9342 -maven/mavencentral/org.springframework.boot/spring-boot-autoconfigure/3.1.3, Apache-2.0, approved, #9341 -maven/mavencentral/org.springframework.boot/spring-boot-configuration-metadata/3.1.3, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.springframework.boot/spring-boot-properties-migrator/3.1.3, Apache-2.0, approved, #10675 -maven/mavencentral/org.springframework.boot/spring-boot-starter-actuator/3.1.3, Apache-2.0, approved, #9344 -maven/mavencentral/org.springframework.boot/spring-boot-starter-aop/3.1.3, Apache-2.0, approved, #9338 -maven/mavencentral/org.springframework.boot/spring-boot-starter-json/3.1.3, Apache-2.0, approved, #9336 -maven/mavencentral/org.springframework.boot/spring-boot-starter-log4j2/3.1.3, Apache-2.0, approved, #8800 -maven/mavencentral/org.springframework.boot/spring-boot-starter-logging/3.1.3, Apache-2.0, approved, #9343 -maven/mavencentral/org.springframework.boot/spring-boot-starter-oauth2-client/3.1.3, Apache-2.0, approved, #8806 -maven/mavencentral/org.springframework.boot/spring-boot-starter-oauth2-resource-server/3.1.3, Apache-2.0, approved, #8804 -maven/mavencentral/org.springframework.boot/spring-boot-starter-security/3.1.3, Apache-2.0, approved, #9337 -maven/mavencentral/org.springframework.boot/spring-boot-starter-test/3.1.3, Apache-2.0, approved, #9353 -maven/mavencentral/org.springframework.boot/spring-boot-starter-tomcat/3.1.3, Apache-2.0, approved, #9351 -maven/mavencentral/org.springframework.boot/spring-boot-starter-validation/3.1.3, Apache-2.0, approved, #9335 -maven/mavencentral/org.springframework.boot/spring-boot-starter-web/3.1.3, Apache-2.0, approved, #9347 -maven/mavencentral/org.springframework.boot/spring-boot-starter/3.1.3, Apache-2.0, approved, #9349 -maven/mavencentral/org.springframework.boot/spring-boot-test-autoconfigure/3.1.3, Apache-2.0, approved, #9339 -maven/mavencentral/org.springframework.boot/spring-boot-test/3.1.3, Apache-2.0, approved, #9346 -maven/mavencentral/org.springframework.boot/spring-boot/3.1.3, Apache-2.0, approved, #9352 -maven/mavencentral/org.springframework.data/spring-data-commons/3.1.3, Apache-2.0, approved, #8805 -maven/mavencentral/org.springframework.security/spring-security-config/6.1.3, Apache-2.0, approved, #9736 -maven/mavencentral/org.springframework.security/spring-security-core/6.1.3, Apache-2.0, approved, #9801 -maven/mavencentral/org.springframework.security/spring-security-crypto/6.1.3, Apache-2.0 AND ISC, approved, #9735 -maven/mavencentral/org.springframework.security/spring-security-oauth2-client/6.1.3, Apache-2.0, approved, #9740 -maven/mavencentral/org.springframework.security/spring-security-oauth2-core/6.1.3, Apache-2.0, approved, #9741 -maven/mavencentral/org.springframework.security/spring-security-oauth2-jose/6.1.3, Apache-2.0, approved, #9345 -maven/mavencentral/org.springframework.security/spring-security-oauth2-resource-server/6.1.3, Apache-2.0, approved, #8798 -maven/mavencentral/org.springframework.security/spring-security-test/6.1.3, Apache-2.0, approved, #10674 -maven/mavencentral/org.springframework.security/spring-security-web/6.1.3, Apache-2.0, approved, #9800 -maven/mavencentral/org.springframework/spring-aop/6.0.11, Apache-2.0, approved, #5940 -maven/mavencentral/org.springframework/spring-beans/6.0.11, Apache-2.0, approved, #5937 -maven/mavencentral/org.springframework/spring-context/6.0.11, Apache-2.0, approved, #5936 -maven/mavencentral/org.springframework/spring-core/6.0.11, Apache-2.0 AND BSD-3-Clause, approved, #5948 -maven/mavencentral/org.springframework/spring-expression/6.0.11, Apache-2.0, approved, #3284 -maven/mavencentral/org.springframework/spring-jcl/6.0.11, Apache-2.0, approved, #3283 -maven/mavencentral/org.springframework/spring-test/6.0.11, Apache-2.0, approved, #7003 -maven/mavencentral/org.springframework/spring-web/6.0.11, Apache-2.0, approved, #5942 -maven/mavencentral/org.springframework/spring-webmvc/6.0.11, Apache-2.0, approved, #5944 +maven/mavencentral/org.springframework.boot/spring-boot-actuator-autoconfigure/3.1.4, Apache-2.0, approved, #9348 +maven/mavencentral/org.springframework.boot/spring-boot-actuator/3.1.4, Apache-2.0, approved, #9342 +maven/mavencentral/org.springframework.boot/spring-boot-autoconfigure/3.1.4, Apache-2.0, approved, #9341 +maven/mavencentral/org.springframework.boot/spring-boot-configuration-metadata/3.1.4, Apache-2.0, approved, #11032 +maven/mavencentral/org.springframework.boot/spring-boot-properties-migrator/3.1.4, Apache-2.0, approved, #10675 +maven/mavencentral/org.springframework.boot/spring-boot-starter-actuator/3.1.4, Apache-2.0, approved, #9344 +maven/mavencentral/org.springframework.boot/spring-boot-starter-aop/3.1.4, Apache-2.0, approved, #9338 +maven/mavencentral/org.springframework.boot/spring-boot-starter-json/3.1.4, Apache-2.0, approved, #9336 +maven/mavencentral/org.springframework.boot/spring-boot-starter-log4j2/3.1.4, Apache-2.0, approved, #8800 +maven/mavencentral/org.springframework.boot/spring-boot-starter-logging/3.1.4, Apache-2.0, approved, #9343 +maven/mavencentral/org.springframework.boot/spring-boot-starter-oauth2-client/3.1.4, Apache-2.0, approved, #8806 +maven/mavencentral/org.springframework.boot/spring-boot-starter-oauth2-resource-server/3.1.4, Apache-2.0, approved, #8804 +maven/mavencentral/org.springframework.boot/spring-boot-starter-security/3.1.4, Apache-2.0, approved, #9337 +maven/mavencentral/org.springframework.boot/spring-boot-starter-test/3.1.4, Apache-2.0, approved, #9353 +maven/mavencentral/org.springframework.boot/spring-boot-starter-tomcat/3.1.4, Apache-2.0, approved, #9351 +maven/mavencentral/org.springframework.boot/spring-boot-starter-validation/3.1.4, Apache-2.0, approved, #9335 +maven/mavencentral/org.springframework.boot/spring-boot-starter-web/3.1.4, Apache-2.0, approved, #9347 +maven/mavencentral/org.springframework.boot/spring-boot-starter/3.1.4, Apache-2.0, approved, #9349 +maven/mavencentral/org.springframework.boot/spring-boot-test-autoconfigure/3.1.4, Apache-2.0, approved, #9339 +maven/mavencentral/org.springframework.boot/spring-boot-test/3.1.4, Apache-2.0, approved, #9346 +maven/mavencentral/org.springframework.boot/spring-boot/3.1.4, Apache-2.0, approved, #9352 +maven/mavencentral/org.springframework.data/spring-data-commons/3.1.4, Apache-2.0, approved, #8805 +maven/mavencentral/org.springframework.security/spring-security-config/6.1.4, Apache-2.0, approved, #9736 +maven/mavencentral/org.springframework.security/spring-security-core/6.1.4, Apache-2.0, approved, #9801 +maven/mavencentral/org.springframework.security/spring-security-crypto/6.1.4, Apache-2.0 AND ISC, approved, #9735 +maven/mavencentral/org.springframework.security/spring-security-oauth2-client/6.1.4, Apache-2.0, approved, #9740 +maven/mavencentral/org.springframework.security/spring-security-oauth2-core/6.1.4, Apache-2.0, approved, #9741 +maven/mavencentral/org.springframework.security/spring-security-oauth2-jose/6.1.4, Apache-2.0, approved, #9345 +maven/mavencentral/org.springframework.security/spring-security-oauth2-resource-server/6.1.4, Apache-2.0, approved, #8798 +maven/mavencentral/org.springframework.security/spring-security-test/6.1.4, Apache-2.0, approved, #10674 +maven/mavencentral/org.springframework.security/spring-security-web/6.1.4, Apache-2.0, approved, #9800 +maven/mavencentral/org.springframework/spring-aop/6.0.12, Apache-2.0, approved, #5940 +maven/mavencentral/org.springframework/spring-beans/6.0.12, Apache-2.0, approved, #5937 +maven/mavencentral/org.springframework/spring-context/6.0.12, Apache-2.0, approved, #5936 +maven/mavencentral/org.springframework/spring-core/6.0.12, Apache-2.0 AND BSD-3-Clause, approved, #5948 +maven/mavencentral/org.springframework/spring-expression/6.0.12, Apache-2.0, approved, #3284 +maven/mavencentral/org.springframework/spring-jcl/6.0.12, Apache-2.0, approved, #3283 +maven/mavencentral/org.springframework/spring-test/6.0.12, Apache-2.0, approved, #7003 +maven/mavencentral/org.springframework/spring-web/6.0.12, Apache-2.0, approved, #5942 +maven/mavencentral/org.springframework/spring-webmvc/6.0.12, Apache-2.0, approved, #5944 maven/mavencentral/org.testcontainers/junit-jupiter/1.18.3, MIT, approved, #7941 maven/mavencentral/org.testcontainers/junit-jupiter/1.19.1, MIT, approved, #10344 maven/mavencentral/org.testcontainers/testcontainers/1.18.3, MIT, approved, #7938 @@ -454,7 +446,6 @@ maven/mavencentral/org.typelevel/spire-macros_2.13/0.17.0, MIT, approved, clearl maven/mavencentral/org.unbescape/unbescape/1.1.6.RELEASE, Apache-2.0, approved, CQ18904 maven/mavencentral/org.webjars/swagger-ui/5.2.0, Apache-2.0, approved, #10221 maven/mavencentral/org.wiremock/wiremock-standalone/3.2.0, MIT AND Apache-2.0, approved, #10919 -maven/mavencentral/org.xerial.snappy/snappy-java/1.1.10.1, Apache-2.0 AND (Apache-2.0 AND BSD-3-Clause), approved, #9098 maven/mavencentral/org.xerial.snappy/snappy-java/1.1.10.5, Apache-2.0 AND (Apache-2.0 AND BSD-3-Clause), approved, #9098 maven/mavencentral/org.xmlunit/xmlunit-core/2.9.1, Apache-2.0, approved, #6272 maven/mavencentral/org.yaml/snakeyaml/1.33, Apache-2.0, approved, clearlydefined diff --git a/charts/irs-helm/CHANGELOG.md b/charts/irs-helm/CHANGELOG.md index 835803ab89..563340dd8b 100644 --- a/charts/irs-helm/CHANGELOG.md +++ b/charts/irs-helm/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- Added configuration for IRS OAuth2 JWT token claim ## [6.8.0] - 2023-10-09 ### Changed diff --git a/charts/irs-helm/templates/configmap-spring-app-config.yaml b/charts/irs-helm/templates/configmap-spring-app-config.yaml index 4a5ce520db..362eb12cec 100644 --- a/charts/irs-helm/templates/configmap-spring-app-config.yaml +++ b/charts/irs-helm/templates/configmap-spring-app-config.yaml @@ -133,6 +133,11 @@ data: apiAllowedBpn: {{ tpl (.Values.bpn | default "") . | quote }} + oauth: + resourceClaim: {{ tpl (.Values.oauth.resourceClaim | default "resource_access") . | quote }} + irsNamespace: {{ tpl (.Values.oauth.irsNamespace | default "") . | quote }} + roles: {{ tpl (.Values.oauth.roles | default "roles") . | quote }} + {{- if .Values.config.content }} {{- tpl (toYaml .Values.config.content) . | nindent 4 }} {{- end }} diff --git a/charts/irs-helm/values.yaml b/charts/irs-helm/values.yaml index eed17bee9f..4e94ddfc84 100644 --- a/charts/irs-helm/values.yaml +++ b/charts/irs-helm/values.yaml @@ -205,6 +205,11 @@ ess: mockRecursiveEdcAsset: # List of BPNs for which the special, mocked notification asset should be used managementPath: "/management/v2" # EDC management API path - used for notification asset creation +oauth: + resourceClaim: "resource_access" # Name of the JWT claim for roles + irsNamespace: "Cl20-CX-IRS" # Namespace for the IRS roles + roles: "roles" # Name of the list of roles within the IRS namespace + config: # If true, the config provided below will completely replace the configmap. # In this case, you need to provide all required config values defined above yourself! diff --git a/docs/src/api/irs-api.yaml b/docs/src/api/irs-api.yaml index cebd16e33d..b2fabc2e2e 100644 --- a/docs/src/api/irs-api.yaml +++ b/docs/src/api/irs-api.yaml @@ -156,6 +156,61 @@ paths: summary: Accepts notifications sent via EDC. tags: - Environmental- and Social Standards + /irs/ess/orders: + post: + description: "Registers an order for an ESS investigation with an array of + {globalAssetIds}. Each globalAssetId will be processed in an separate job, grouped in batches." + operationId: registerESSInvestigationOrder + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterBpnInvestigationBatchOrder' + required: true + responses: + "201": + content: + application/json: + examples: + complete: + $ref: '#/components/examples/job-handle' + schema: + $ref: '#/components/schemas/BatchOrderCreated' + description: Returns orderId of registered Batch order. + "400": + content: + application/json: + examples: + error: + $ref: '#/components/examples/error-response-400' + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Batch Order registration failed. + "401": + content: + application/json: + examples: + error: + $ref: '#/components/examples/error-response-401' + schema: + $ref: '#/components/schemas/ErrorResponse' + description: No valid authentication credentials. + "403": + content: + application/json: + examples: + error: + $ref: '#/components/examples/error-response-403' + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Authorization refused by server. + security: + - oAuth2: + - profile email + summary: "Registers an order for an ESS investigation with an array of {globalAssetIds}. + Each globalAssetId will be processed in an separate job, grouped in batches." + tags: + - Environmental- and Social Standards /irs/jobs: get: description: Returns paginated jobs with state and execution times. @@ -759,7 +814,7 @@ paths: responses: "200": content: - 'application/json': + application/json: schema: type: array items: @@ -1155,8 +1210,8 @@ components: - language: en text: The shell for a vehicle globalAssetId: urn:uuid:a45a2246-f6e1-42da-b47d-5c3b58ed62e9 - idShort: future concept x id: 882fc530-b69b-4707-95f6-5dbc5e9baaa8 + idShort: future concept x specificAssetIds: - name: engineserialid value: "12309481209312" @@ -1186,13 +1241,13 @@ components: href: https://catena-x.net/vehicle/partdetails/ endpointProtocol: HTTPS endpointProtocolVersion: ["1.0"] - idShort: vehicle part details id: dae4d249-6d66-4818-b576-bf52f3b9ae90 + idShort: vehicle part details semanticId: - type: ModelReference keys: - type: Submodel value: urn:bamm:com.catenax.vehicle:0.1.1#PartDetails + type: ModelReference submodels: - aspectType: urn:bamm:io.catenax.single_level_bom_as_built:1.0.0 identification: urn:uuid:fc784d2a-5506-4e61-8e34-21600f8cdeff @@ -1335,8 +1390,8 @@ components: - language: en text: The shell for a vehicle globalAssetId: urn:uuid:a45a2246-f6e1-42da-b47d-5c3b58ed62e9 - idShort: future concept x id: 882fc530-b69b-4707-95f6-5dbc5e9baaa8 + idShort: future concept x specificAssetIds: - name: engineserialid value: "12309481209312" @@ -1350,8 +1405,8 @@ components: href: https://catena-x.net/vehicle/basedetails/ endpointProtocol: HTTPS endpointProtocolVersion: ["1.0"] - idShort: vehicle base details id: 4a738a24-b7d8-4989-9cd6-387772f40565 + idShort: vehicle base details semanticId: type: ModelReference keys: @@ -1366,8 +1421,8 @@ components: href: https://catena-x.net/vehicle/partdetails/ endpointProtocol: HTTPS endpointProtocolVersion: ["1.0"] - idShort: vehicle part details id: dae4d249-6d66-4818-b576-bf52f3b9ae90 + idShort: vehicle part details semanticId: type: ModelReference keys: @@ -1468,10 +1523,10 @@ components: type: string description: Id of global asset. example: urn:uuid:6c311d29-5753-46d4-b32c-19b918ea93b0 - idShort: - type: string id: type: string + idShort: + type: string specificAssetIds: type: array items: @@ -1507,8 +1562,8 @@ components: minimum: 0 BatchOrderCreated: type: object - description: Id of the created Batch order. additionalProperties: false + description: Id of the created Batch order. properties: id: type: string @@ -2258,6 +2313,69 @@ components: minimum: 60 required: - keys + RegisterBpnInvestigationBatchOrder: + type: object + additionalProperties: false + description: Request body for registering a new Batch Order for ESS Investigation + Job. + properties: + batchSize: + type: integer + format: int32 + default: 20 + description: Size of the batch. + maximum: 100 + minimum: 10 + batchStrategy: + type: string + description: The strategy how the batch is processed internally in IRS. + enum: + - PRESERVE_BATCH_JOB_ORDER + - PRESERVE_JOB_ORDER + - PRESERVE_BATCH_ORDER + bomLifecycle: + type: string + description: The lifecycle context in which the child part was assembled + into the parent part. + enum: + - asBuilt + - asPlanned + - asSpecified + callbackUrl: + type: string + description: "Callback url to notify requestor when job processing is finished.\ + \ There are four uri variable placeholders that can be used: orderId,\ + \ batchId, orderState and batchState." + example: "https://hostname.com/callback?orderId={orderId}&batchId={batchId}&orderState={orderState}&batchState={batchState}" + incidentBPNSs: + type: array + items: + type: string + pattern: "(BPN)[LSA][\\w\\d]{10}[\\w\\d]{2}" + maxItems: 2147483647 + jobTimeout: + type: integer + format: int32 + default: 3600 + description: Timeout in seconds for each job processing inside the complete + order. + maximum: 7200 + minimum: 60 + keys: + type: array + items: + $ref: '#/components/schemas/PartChainIdentificationKey' + maxItems: 2147483647 + timeout: + type: integer + format: int32 + default: 43200 + description: Timeout in seconds for the complete batch order processing. + maximum: 86400 + minimum: 60 + required: + - incidentBPNSs + - keys RegisterBpnInvestigationJob: type: object additionalProperties: false diff --git a/docs/src/docs/arc42/building-block-view/level-2.adoc b/docs/src/docs/arc42/building-block-view/level-2.adoc index 025ec52d51..c0f3310909 100644 --- a/docs/src/docs/arc42/building-block-view/level-2.adoc +++ b/docs/src/docs/arc42/building-block-view/level-2.adoc @@ -85,7 +85,7 @@ include::../../../uml-diagrams/building-block-view/level-2-int-transfer-process- |The DigitalTwinRegistryFacade calls the DigitalTwinRegistry to retrieve data form the AAS registry and transforms the response to internal data models. |SubmodelFacade -|The SubmodelFacade calls the EDC to retrieve data from the submodel server and transforms the response to internal data models. +|The SubmodelFacade handles EDC contract negotiations and is responsible for the EDC dataplane requests to retrieve data from the submodel servers. |BlobStore |The BlobStore is the database where the relationships and tombstones are stored for a requested item. diff --git a/docs/src/docs/arc42/cross-cutting/safety-security.adoc b/docs/src/docs/arc42/cross-cutting/safety-security.adoc index 0d425606e3..a5e7faee62 100644 --- a/docs/src/docs/arc42/cross-cutting/safety-security.adoc +++ b/docs/src/docs/arc42/cross-cutting/safety-security.adoc @@ -1,12 +1,19 @@ = Safety and security concepts == Authentication / Authorization + === IRS API -The IRS is secured using OAuth2.0 / Open ID Connect. Every request to the IRS API requires a valid bearer token. + +The IRS is secured using OAuth2.0 / Open ID Connect. +Every request to the IRS API requires a valid bearer token. JWT token should also contain two claims: - 'bpn' which is equal to the configuration value from `API_ALLOWED_BPN` property -- 'resource_access' with the specific 'Cl20-CX-IRS' key for C-X environments. The list of values will be converted to roles by IRS. Currently, IRS API handles two roles: **'admin_irs'** and **'view_irs'.** A valid token with the **'admin_irs'** role can access any endpoint exposed by the IRS API, while a token with the **'view_irs'** role does not have access to policies endpoints and can operate only on resources it owns. That means that he only has access to the resources he has created, e.g. jobs and batches. This behavior is shown in the table below. +- 'resource_access' with the specific 'Cl20-CX-IRS' key for C-X environments. (The keys are configurable. For more details see chapter "IRS OAuth2 JWT Token"). +The list of values will be converted to roles by IRS. +Currently, IRS API handles two roles: **'admin_irs'** and **'view_irs'.** A valid token with the **'admin_irs'** role can access any endpoint exposed by the IRS API, while a token with the **'view_irs'** role does not have access to policies endpoints and can operate only on resources it owns. +That means that he only has access to the resources he has created, e.g. jobs and batches. +This behavior is shown in the table below. ==== Rights and Roles Matrix of IRS @@ -30,20 +37,60 @@ Social Standards | Register investigation job | POST /ess/bpn/investigations | | Get investigation job | GET /ess/bpn/investigations{id} | (x) | x | | Accept notifications | POST /ess/notification/receive | x | x |=== -Legend: x = full access to all resources, (x) = access to the resources he owns + +Legend: x = full access to all resources, (x) = access to the resources he owns + +=== IRS OAuth2 JWT Token + +IRS expects the JWT access token to have the following structure to be able to extract role information: + +[source,json] +---- +{ +... + "resource_access": { + "Cl20-CX-IRS": { + "roles": [ + "view_irs", + "admin_irs" + ] + } + }, +... +} +---- + +The field names can be configured via application.yaml: + +[source,yaml] +---- +# OAuth2 JWT token parse config. This configures the structure IRS expects when parsing the IRS role of an access token. +oauth: + resourceClaim: "resource_access" # Name of the JWT claim for roles + irsNamespace: "Cl20-CX-IRS" # Namespace for the IRS roles + roles: "roles" # Name of the list of roles within the IRS namespace +---- === IRS as DTR client -The IRS acts as a client for the Digital Twin Registry (DTR), which is also secured using OAuth2.0 / Open ID Connect. The IRS uses client credentials to authenticate requests to the DTR. Due to this, the IRS account needs to have access to every item in the DTR, unrelated to the permissions of the account calling the IRS API. + +The IRS acts as a client for the Digital Twin Registry (DTR), which is also secured using OAuth2.0 / Open ID Connect. +The IRS uses client credentials to authenticate requests to the DTR. +Due to this, the IRS account needs to have access to every item in the DTR, unrelated to the permissions of the account calling the IRS API. === IRS as decentralized DTR client -In a decentralized network, IRS uses the EDC client to access the provider DTR. This way, no authentication, other than the EDC contract negotiation, is needed to access the DTR. + +In a decentralized network, IRS uses the EDC client to access the provider DTR. +This way, no authentication, other than the EDC contract negotiation, is needed to access the DTR. === IRS as EDC client -The IRS accesses the Catena-X network via the EDC consumer connector. This component requires authentication via a Verifiable Credential (VC), which is provided to the EDC via the Managed Identity Wallet. + +The IRS accesses the Catena-X network via the EDC consumer connector. +This component requires authentication via a Verifiable Credential (VC), which is provided to the EDC via the Managed Identity Wallet. The VC identifies and authenticates the EDC and is used to acquire access permissions for the data transferred via EDC. == Credentials + Credentials must never be stored in Git! diff --git a/docs/src/docs/arc42/cross-cutting/under-the-hood.adoc b/docs/src/docs/arc42/cross-cutting/under-the-hood.adoc index d01fee8541..63f3a3034c 100644 --- a/docs/src/docs/arc42/cross-cutting/under-the-hood.adoc +++ b/docs/src/docs/arc42/cross-cutting/under-the-hood.adoc @@ -74,6 +74,14 @@ The IRS gives its users the ability to manage, create and delete complex policie The structure of a Policy that can be stored in storage can be easily viewed by using Policy Store endpoints in the published API documentation. Each policy may contain more than one permission, which in turn consists of constraints linked together by AND or OR relationships. This model provides full flexibility and control over stored access and use policies. +== Digital Twin / EDC requirements + +In order to work with the decentral network approach, IRS requires the Digital Twin to contain a `"subprotocolBody"` in each of the submodelDescriptor endpoints. This `"subprotocolBody"` has to contain the `"id"` of the EDC asset, as well as the `"dspEndpoint"` of the EDC, separated by a semicolon (e.g. `"subprotocolBody": "id=123;dspEndpoint=http://edc.control.plane/api/v1/dsp"`). + +The `"dspEndpoint"` is used to request the EDC catalog of the dataprovider and the `"id"` to filter for the exact asset inside this catalog. + +If the `"dspEndpoint"` is not present, every available EDC endpoint in DiscoveryService will be queried until a asset with the `"id"` can be found. + == Caching The IRS caches data provided externally to avoid unnecessary requests and reduce execution time. diff --git a/irs-api/pom.xml b/irs-api/pom.xml index 7033434d67..9c546a88f4 100644 --- a/irs-api/pom.xml +++ b/irs-api/pom.xml @@ -45,19 +45,10 @@ io.minio minio - ${minio.version} - - - org.bouncycastle - bcprov-jdk15on - - - org.xerial.snappy snappy-java - 1.1.10.5 com.squareup.okhttp3 @@ -170,16 +161,12 @@ + org.jsoup jsoup ${jsoup.version} - - org.graalvm.sdk - graal-sdk - ${graal-sdk.version} - org.eclipse.tractusx.irs diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/IrsApplication.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/IrsApplication.java index 8b70400395..9d4bbbf566 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/IrsApplication.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/IrsApplication.java @@ -26,6 +26,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration; import org.springframework.boot.context.TypeExcludeFilter; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.ComponentScan; @@ -36,7 +37,7 @@ /** * Application entry point. */ -@SpringBootApplication +@SpringBootApplication(exclude = WebSocketServletAutoConfiguration.class) @EnableScheduling @EnableCaching @EnableAsync @@ -50,6 +51,7 @@ excludeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)}) +@SuppressWarnings("PMD.UseUtilityClass") public class IrsApplication { /** diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/SecurityConfiguration.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/SecurityConfiguration.java index a64f7ccb36..d7a46ab4bd 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/SecurityConfiguration.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/SecurityConfiguration.java @@ -29,7 +29,9 @@ import java.util.List; import org.eclipse.tractusx.irs.common.ApiConstants; +import org.eclipse.tractusx.irs.configuration.converter.IrsTokenParser; import org.eclipse.tractusx.irs.configuration.converter.JwtAuthenticationConverter; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.Customizer; @@ -38,6 +40,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; +import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter; import org.springframework.security.web.header.writers.PermissionsPolicyHeaderWriter; @@ -76,7 +79,8 @@ public class SecurityConfiguration { @SuppressWarnings("PMD.SignatureDeclareThrowsException") @Bean - /* package */ SecurityFilterChain securityFilterChain(final HttpSecurity httpSecurity) throws Exception { + /* package */ SecurityFilterChain securityFilterChain(final HttpSecurity httpSecurity, + final JwtAuthenticationConverter jwtAuthenticationConverter) throws Exception { httpSecurity.httpBasic(AbstractHttpConfigurer::disable); httpSecurity.formLogin(AbstractHttpConfigurer::disable); httpSecurity.csrf(AbstractHttpConfigurer::disable); @@ -111,7 +115,7 @@ public class SecurityConfiguration { httpSecurity.oauth2ResourceServer(oauth2ResourceServer -> oauth2ResourceServer.jwt(jwt -> - jwt.jwtAuthenticationConverter(new JwtAuthenticationConverter()))) + jwt.jwtAuthenticationConverter(jwtAuthenticationConverter))) .oauth2Client(Customizer.withDefaults()); return httpSecurity.build(); @@ -131,4 +135,15 @@ public class SecurityConfiguration { return source; } + @Bean + /* package */ IrsTokenParser irsTokenParser(@Value("${oauth.resourceClaim}") final String resourceAccessClaim, + @Value("${oauth.irsNamespace}") final String irsResourceAccess, + @Value("${oauth.roles}") final String roles) { + return new IrsTokenParser(resourceAccessClaim, irsResourceAccess, roles); + } + + @Bean + /* package */ JwtAuthenticationConverter jwtAuthenticationConverter(final IrsTokenParser irsTokenParser) { + return new JwtAuthenticationConverter(new JwtGrantedAuthoritiesConverter(), irsTokenParser); + } } diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/converter/IrsTokenParser.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/converter/IrsTokenParser.java new file mode 100644 index 0000000000..198e767930 --- /dev/null +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/converter/IrsTokenParser.java @@ -0,0 +1,66 @@ +/******************************************************************************** + * Copyright (c) 2021,2022,2023 + * 2022: ZF Friedrichshafen AG + * 2022: ISTOS GmbH + * 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 2022,2023: BOSCH AG + * Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.configuration.converter; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import com.nimbusds.jose.shaded.gson.internal.LinkedTreeMap; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.jwt.Jwt; + +/** + * Parsing JWT - retrieving resource_access claim with IRS roles. + */ +@Slf4j +@AllArgsConstructor +public class IrsTokenParser { + + private String resourceAccessClaim; + private String irsResourceAccess; + private String roles; + + /** + * Parsing JWT - retrieving resource_access claim with IRS roles. + * + * @param jwt source + * @return set of roles from token + */ + public Set extractIrsRolesFromToken(final Jwt jwt) { + return Optional.ofNullable(jwt.getClaim(resourceAccessClaim)) + .map(LinkedTreeMap.class::cast) + .map(accesses -> accesses.get(irsResourceAccess)) + .map(LinkedTreeMap.class::cast) + .map(irsAccesses -> irsAccesses.get(roles)) + .map(irsRoles -> ((List) irsRoles).stream() + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toSet())) + .orElse(Collections.emptySet()); + } +} diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/converter/JwtAuthenticationConverter.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/converter/JwtAuthenticationConverter.java index 915cb9704c..81f4b71ced 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/converter/JwtAuthenticationConverter.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/converter/JwtAuthenticationConverter.java @@ -24,20 +24,14 @@ package org.eclipse.tractusx.irs.configuration.converter; import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import com.nimbusds.jose.shaded.gson.internal.LinkedTreeMap; -import lombok.extern.slf4j.Slf4j; +import lombok.AllArgsConstructor; import org.jetbrains.annotations.NotNull; import org.springframework.core.convert.converter.Converter; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; @@ -45,10 +39,11 @@ /** * JWT Converter */ +@AllArgsConstructor public class JwtAuthenticationConverter implements Converter { - private final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); - private final IrsTokenParser irsTokenParser = new IrsTokenParser(); + private JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter; + private IrsTokenParser irsTokenParser; @Override public AbstractAuthenticationToken convert(final @NotNull Jwt source) { @@ -60,35 +55,5 @@ public AbstractAuthenticationToken convert(final @NotNull Jwt source) { return new JwtAuthenticationToken(source, authorities); } - - /** - * Parsing JWT - retrieving resource_access claim with IRS roles. - */ - @Slf4j - /* package */ static class IrsTokenParser { - - private static final String RESOURCE_ACCESS_CLAIM = "resource_access"; - private static final String IRS_RESOURCE_ACCESS = "Cl20-CX-IRS"; - private static final String ROLES = "roles"; - - /** - * Parsing JWT - retrieving resource_access claim with IRS roles. - * - * @param jwt source - * @return list of roles from token - */ - public Set extractIrsRolesFromToken(final Jwt jwt) { - return Optional.ofNullable(jwt.getClaim(RESOURCE_ACCESS_CLAIM)) - .map(LinkedTreeMap.class::cast) - .map(accesses -> accesses.get(IRS_RESOURCE_ACCESS)) - .map(LinkedTreeMap.class::cast) - .map(irsAccesses -> irsAccesses.get(ROLES)) - .map(roles -> ((List) roles).stream() - .map(SimpleGrantedAuthority::new) - .collect(Collectors.toSet())) - .orElse(Collections.emptySet()); - } - } - } diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/connector/batch/BatchOrder.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/connector/batch/BatchOrder.java index d35766f4fa..9d47f918f0 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/connector/batch/BatchOrder.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/connector/batch/BatchOrder.java @@ -44,65 +44,41 @@ @Jacksonized public class BatchOrder { - /** - * Batch Order Id - */ private UUID batchOrderId; - /** - * Processing State of Batch Order - */ @Setter private ProcessingState batchOrderState; - /** - * Bom Lifecycle requested in order - */ private BomLifecycle bomLifecycle; - /** - * List of Aspects requested in order - */ private List aspects; - /** - * Depth requested in order - */ private Integer depth; - /** - * Direction requested in order - */ private Direction direction; - /** - * Needs of collect aspects - */ private Boolean collectAspects; - /** - * Flag to specify whether BPNs should be collected and resolved via the configured BPDM URL - */ private Boolean lookupBPNs; - /** - * Timeout for Batch Order - */ private Integer timeout; - /** - * Timeout for Job in Batch - */ private Integer jobTimeout; - /** - * Callback Url to send results - */ private String callbackUrl; + private String owner; + + private List incidentBPNSs; + + private JobType jobType; + /** - * Owner + * JobType to create batch */ - private String owner; + public enum JobType { + REGULAR, + ESS + } } diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/connector/job/MultiTransferJob.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/connector/job/MultiTransferJob.java index 2b5f297552..bc65e09724 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/connector/job/MultiTransferJob.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/connector/job/MultiTransferJob.java @@ -106,6 +106,10 @@ public JobParameter getJobParameter() { return getJob().getParameter(); } + public boolean jobIsCompleted() { + return this.getJob().getState().equals(JobState.COMPLETED); + } + /** * Builder for {@link MultiTransferJob}. */ diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/BatchController.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/BatchController.java index 92bf01f496..7e4a5aae62 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/BatchController.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/BatchController.java @@ -47,6 +47,7 @@ import org.eclipse.tractusx.irs.component.BatchOrderResponse; import org.eclipse.tractusx.irs.component.BatchResponse; import org.eclipse.tractusx.irs.component.RegisterBatchOrder; +import org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder; import org.eclipse.tractusx.irs.dtos.ErrorResponse; import org.eclipse.tractusx.irs.common.auth.AuthorizationService; import org.eclipse.tractusx.irs.services.CreationBatchService; @@ -121,6 +122,45 @@ public BatchOrderCreated registerBatchOrder(final @Valid @RequestBody RegisterBa return BatchOrderCreated.builder().id(batchOrderId).build(); } + @Operation(operationId = "registerESSInvestigationOrder", + summary = "Registers an order for an ESS investigation with an array of {globalAssetIds}. Each globalAssetId will be processed in an separate job, grouped in batches.", + security = @SecurityRequirement(name = "oAuth2", scopes = "profile email"), + tags = { "Environmental- and Social Standards" }, + description = "Registers an order for an ESS investigation with an array of {globalAssetIds}. Each globalAssetId will be processed in an separate job, grouped in batches.") + @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "Returns orderId of registered Batch order.", + content = { @Content(mediaType = APPLICATION_JSON_VALUE, + schema = @Schema(implementation = BatchOrderCreated.class), + examples = { @ExampleObject(name = "complete", + ref = "#/components/examples/job-handle") + }) + }), + @ApiResponse(responseCode = "400", description = "Batch Order registration failed.", + content = { @Content(mediaType = APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ErrorResponse.class), + examples = @ExampleObject(name = "error", + ref = "#/components/examples/error-response-400")) + }), + @ApiResponse(responseCode = "401", description = UNAUTHORIZED_DESC, + content = { @Content(mediaType = APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ErrorResponse.class), + examples = @ExampleObject(name = "error", + ref = "#/components/examples/error-response-401")) + }), + @ApiResponse(responseCode = "403", description = FORBIDDEN_DESC, + content = { @Content(mediaType = APPLICATION_JSON_VALUE, + schema = @Schema(implementation = ErrorResponse.class), + examples = @ExampleObject(name = "error", + ref = "#/components/examples/error-response-403")) + }), + }) + @PostMapping("/ess/orders") + @ResponseStatus(HttpStatus.CREATED) + @PreAuthorize("@authorizationService.verifyBpn() && hasAnyAuthority('" + IrsRoles.ADMIN_IRS + "', '" + IrsRoles.VIEW_IRS + "')") + public BatchOrderCreated registerESSInvestigationOrder(final @Valid @RequestBody RegisterBpnInvestigationBatchOrder request) { + final UUID batchOrderId = creationBatchService.create(request); + return BatchOrderCreated.builder().id(batchOrderId).build(); + } + @Operation(description = "Get a batch order for a given orderId.", operationId = "getBatchOrder", summary = "Get a batch order for a given orderId.", diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsController.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsController.java index 2cda391451..129b733c30 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsController.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsController.java @@ -275,10 +275,8 @@ public PageResult getJobsByState( explode = Explode.FALSE, array = @ArraySchema(schema = @Schema(implementation = JobState.class), maxItems = Integer.MAX_VALUE)) @RequestParam(value = "states", required = false, defaultValue = "") final List states, @Parameter(hidden = true) - @RequestParam(value = "jobStates", required = false, defaultValue = "") final List jobStates, - @Parameter(hidden = true) @ParameterObject final Pageable pageable) { - return itemJobService.getJobsByState(states, jobStates, pageable); + return itemJobService.getJobsByState(states, pageable); } @Operation(operationId = "getAllAspectModels", diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/service/BpnInvestigationJob.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/service/BpnInvestigationJob.java index e95dc60fea..a97131b388 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/service/BpnInvestigationJob.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/service/BpnInvestigationJob.java @@ -55,17 +55,12 @@ public class BpnInvestigationJob { private List answeredNotifications; private JobState state; - public static BpnInvestigationJob create(final Jobs jobSnapshot, final String owner, + public static BpnInvestigationJob create(final Jobs jobSnapshot, final List incidentBpns) { - return new BpnInvestigationJob(withOwner(jobSnapshot, owner), incidentBpns, new ArrayList<>(), + return new BpnInvestigationJob(jobSnapshot, incidentBpns, new ArrayList<>(), new ArrayList<>(), JobState.RUNNING); } - private static Jobs withOwner(final Jobs jobSnapshot, final String owner) { - final Job overrideOwner = jobSnapshot.getJob().toBuilder().owner(owner).build(); - return jobSnapshot.toBuilder().job(overrideOwner).build(); - } - private static Jobs extendJobWithSupplyChainSubmodel(final Jobs irsJob, final SupplyChainImpacted supplyChainImpacted) { final Submodel supplyChainImpactedSubmodel = Submodel.builder() @@ -91,10 +86,8 @@ public BpnInvestigationJob update(final Jobs jobSnapshot, final SupplyChainImpac final SupplyChainImpacted supplyChainImpacted = previousSupplyChain.map( prevSupplyChain -> prevSupplyChain.or(newSupplyChain)).orElse(newSupplyChain); - final String originalOwner = this.jobSnapshot.getJob().getOwner(); this.jobSnapshot = extendJobWithSupplyChainSubmodel(jobSnapshot, supplyChainImpacted); this.jobSnapshot = extendSummary(this.jobSnapshot); - this.jobSnapshot = withOwner(this.jobSnapshot, originalOwner); this.jobSnapshot = updateLastModified(this.jobSnapshot, ZonedDateTime.now(ZoneOffset.UTC)); return this; } diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/service/EssService.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/service/EssService.java index 5e920ddc1d..15d7b83971 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/service/EssService.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/service/EssService.java @@ -39,6 +39,8 @@ import org.eclipse.tractusx.irs.component.enums.AspectType; import org.eclipse.tractusx.irs.component.enums.BomLifecycle; import org.eclipse.tractusx.irs.component.enums.JobState; +import org.eclipse.tractusx.irs.connector.job.JobStore; +import org.eclipse.tractusx.irs.connector.job.MultiTransferJob; import org.eclipse.tractusx.irs.edc.client.model.notification.EdcNotification; import org.eclipse.tractusx.irs.edc.client.model.notification.ResponseNotificationContent; import org.eclipse.tractusx.irs.services.IrsItemGraphQueryService; @@ -57,44 +59,22 @@ public class EssService { private final IrsItemGraphQueryService irsItemGraphQueryService; private final SecurityHelperService securityHelperService; private final BpnInvestigationJobCache bpnInvestigationJobCache; + private final JobStore jobStore; private final EssRecursiveNotificationHandler recursiveNotificationHandler; - private static Jobs updateState(final BpnInvestigationJob investigationJob) { - final Jobs jobSnapshot = investigationJob.getJobSnapshot(); - log.debug("Unanswered Notifications '{}'", investigationJob.getUnansweredNotifications()); - log.debug("Answered Notifications '{}'", investigationJob.getAnsweredNotifications()); - - final JobState jobState = updateJobState(investigationJob); - - return jobSnapshot.toBuilder().job(jobSnapshot.getJob().toBuilder().state(jobState).build()).build(); - } - - private static JobState updateJobState(final BpnInvestigationJob investigationJob) { - if (hasUnansweredNotifications(investigationJob)) { - return JobState.RUNNING; - } - if (hasAnsweredNotifications(investigationJob)) { - return JobState.COMPLETED; - } - return investigationJob.getState(); - } - - private static boolean hasAnsweredNotifications(final BpnInvestigationJob investigationJob) { - return !investigationJob.getAnsweredNotifications().isEmpty(); - } - - private static boolean hasUnansweredNotifications(final BpnInvestigationJob investigationJob) { - return !investigationJob.getUnansweredNotifications().isEmpty(); + public JobHandle startIrsJob(final RegisterBpnInvestigationJob request) { + return startIrsJob(request, null, securityHelperService.getClientIdClaim()); } - public JobHandle startIrsJob(final RegisterBpnInvestigationJob request) { - final JobHandle jobHandle = irsItemGraphQueryService.registerItemJob(bpnInvestigations(request.getKey(), request.getBomLifecycle())); + public JobHandle startIrsJob(final RegisterBpnInvestigationJob request, final UUID batchId, final String owner) { + final JobHandle jobHandle = irsItemGraphQueryService.registerItemJob(bpnInvestigations(request.getKey(), request.getBomLifecycle()), batchId, owner); final UUID createdJobId = jobHandle.getId(); - final Jobs createdJob = irsItemGraphQueryService.getJobForJobId(createdJobId, true); - final String owner = securityHelperService.getClientIdClaim(); - bpnInvestigationJobCache.store(createdJobId, - BpnInvestigationJob.create(createdJob, owner, request.getIncidentBPNSs())); + final Optional multiTransferJob = jobStore.find(createdJobId.toString()); + multiTransferJob.ifPresent(job -> { + final Jobs createdJob = irsItemGraphQueryService.getJobForJobId(job, true); + bpnInvestigationJobCache.store(createdJobId, BpnInvestigationJob.create(createdJob, request.getIncidentBPNSs())); + }); return jobHandle; } @@ -149,6 +129,34 @@ public void handleNotificationCallback(final EdcNotification investigationJobNotificationPredicate( final EdcNotification notification) { return investigationJob -> investigationJob.getUnansweredNotifications() @@ -165,4 +173,5 @@ private RegisterJob bpnInvestigations(final PartChainIdentificationKey key, fina .collectAspects(true) .build(); } + } diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/service/InvestigationJobProcessingEventListener.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/service/InvestigationJobProcessingEventListener.java index 5ebd486a1e..dd9151b495 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/service/InvestigationJobProcessingEventListener.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/ess/service/InvestigationJobProcessingEventListener.java @@ -38,6 +38,8 @@ import org.eclipse.tractusx.irs.component.enums.AspectType; import org.eclipse.tractusx.irs.component.partasplanned.PartAsPlanned; import org.eclipse.tractusx.irs.component.partsiteinformationasplanned.PartSiteInformationAsPlanned; +import org.eclipse.tractusx.irs.connector.job.JobStore; +import org.eclipse.tractusx.irs.connector.job.MultiTransferJob; import org.eclipse.tractusx.irs.data.StringMapper; import org.eclipse.tractusx.irs.edc.client.EdcSubmodelFacade; import org.eclipse.tractusx.irs.edc.client.exceptions.EdcClientException; @@ -70,6 +72,7 @@ class InvestigationJobProcessingEventListener { private final ConnectorEndpointsService connectorEndpointsService; private final EdcSubmodelFacade edcSubmodelFacade; private final BpnInvestigationJobCache bpnInvestigationJobCache; + private final JobStore jobStore; private final String localBpn; private final String localEdcEndpoint; private final List mockRecursiveEdcAssets; @@ -77,7 +80,8 @@ class InvestigationJobProcessingEventListener { /* package */ InvestigationJobProcessingEventListener(final IrsItemGraphQueryService irsItemGraphQueryService, final ConnectorEndpointsService connectorEndpointsService, final EdcSubmodelFacade edcSubmodelFacade, - final BpnInvestigationJobCache bpnInvestigationJobCache, @Value("${ess.localBpn}") final String localBpn, + final BpnInvestigationJobCache bpnInvestigationJobCache, final JobStore jobStore, + @Value("${ess.localBpn}") final String localBpn, @Value("${ess.localEdcEndpoint}") final String localEdcEndpoint, @Value("${ess.discovery.mockRecursiveEdcAsset}") final List mockRecursiveEdcAssets, final EssRecursiveNotificationHandler recursiveNotificationHandler) { @@ -85,6 +89,7 @@ class InvestigationJobProcessingEventListener { this.connectorEndpointsService = connectorEndpointsService; this.edcSubmodelFacade = edcSubmodelFacade; this.bpnInvestigationJobCache = bpnInvestigationJobCache; + this.jobStore = jobStore; this.localBpn = localBpn; this.localEdcEndpoint = localEdcEndpoint; this.mockRecursiveEdcAssets = mockRecursiveEdcAssets; @@ -168,31 +173,35 @@ public void handleJobProcessingFinishedEvent(final JobProcessingFinishedEvent jo bpnInvestigationJob.ifPresent(investigationJob -> { log.info("Job is completed. Starting SupplyChainImpacted processing for job {}.", completedJobId); - final Jobs completedJob = irsItemGraphQueryService.getJobForJobId(completedJobId, false); - final SupplyChainImpacted partAsPlannedValidity = validatePartAsPlanned(completedJob); - log.info("Local validation of PartAsPlanned Validity was done for job {}. with result {}.", completedJobId, - partAsPlannedValidity); - final SupplyChainImpacted partSiteInformationAsPlannedValidity = validatePartSiteInformationAsPlanned( - investigationJob, completedJob); - log.info("Local validation of PartSiteInformationAsPlanned Validity was done for job {}. with result {}.", - completedJobId, partSiteInformationAsPlannedValidity); - - final SupplyChainImpacted supplyChainImpacted = partAsPlannedValidity.or( - partSiteInformationAsPlannedValidity); - log.debug("Supply Chain Validity result of {} and {} resulted in {}", partAsPlannedValidity, - partSiteInformationAsPlannedValidity, supplyChainImpacted); - - final BpnInvestigationJob investigationJobUpdate = investigationJob.update(completedJob, - supplyChainImpacted); - - if (leafNodeIsReached(completedJob) || supplyChainIsImpacted(supplyChainImpacted)) { - bpnInvestigationJobCache.store(completedJobId, investigationJobUpdate.complete()); - recursiveNotificationHandler.handleNotification(investigationJob.getJobSnapshot().getJob().getId(), + final Optional multiTransferJob = jobStore.find(completedJobId.toString()); + multiTransferJob.ifPresent(job -> { + final Jobs completedJob = irsItemGraphQueryService.getJobForJobId(job, false); + final SupplyChainImpacted partAsPlannedValidity = validatePartAsPlanned(completedJob); + log.info("Local validation of PartAsPlanned Validity was done for job {}. with result {}.", completedJobId, + partAsPlannedValidity); + final SupplyChainImpacted partSiteInformationAsPlannedValidity = validatePartSiteInformationAsPlanned( + investigationJob, completedJob); + log.info("Local validation of PartSiteInformationAsPlanned Validity was done for job {}. with result {}.", + completedJobId, partSiteInformationAsPlannedValidity); + + final SupplyChainImpacted supplyChainImpacted = partAsPlannedValidity.or( + partSiteInformationAsPlannedValidity); + log.debug("Supply Chain Validity result of {} and {} resulted in {}", partAsPlannedValidity, + partSiteInformationAsPlannedValidity, supplyChainImpacted); + + final BpnInvestigationJob investigationJobUpdate = investigationJob.update(completedJob, supplyChainImpacted); - } else { - triggerInvestigationOnNextLevel(completedJob, investigationJobUpdate); - bpnInvestigationJobCache.store(completedJobId, investigationJobUpdate); - } + + if (leafNodeIsReached(completedJob) || supplyChainIsImpacted(supplyChainImpacted)) { + bpnInvestigationJobCache.store(completedJobId, investigationJobUpdate.complete()); + recursiveNotificationHandler.handleNotification(investigationJob.getJobSnapshot().getJob().getId(), + supplyChainImpacted); + } else { + triggerInvestigationOnNextLevel(completedJob, investigationJobUpdate); + bpnInvestigationJobCache.store(completedJobId, investigationJobUpdate); + } + }); + }); } diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/services/BatchOrderEventListener.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/services/BatchOrderEventListener.java index c162bc4f54..6214fd9d8e 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/services/BatchOrderEventListener.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/services/BatchOrderEventListener.java @@ -31,6 +31,7 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.irs.component.JobHandle; import org.eclipse.tractusx.irs.component.PartChainIdentificationKey; +import org.eclipse.tractusx.irs.component.RegisterBpnInvestigationJob; import org.eclipse.tractusx.irs.component.RegisterJob; import org.eclipse.tractusx.irs.component.enums.JobState; import org.eclipse.tractusx.irs.component.enums.ProcessingState; @@ -39,6 +40,7 @@ import org.eclipse.tractusx.irs.connector.batch.BatchOrderStore; import org.eclipse.tractusx.irs.connector.batch.BatchStore; import org.eclipse.tractusx.irs.connector.batch.JobProgress; +import org.eclipse.tractusx.irs.ess.service.EssService; import org.eclipse.tractusx.irs.services.events.BatchOrderProcessingFinishedEvent; import org.eclipse.tractusx.irs.services.events.BatchOrderRegisteredEvent; import org.eclipse.tractusx.irs.services.events.BatchProcessingFinishedEvent; @@ -59,6 +61,7 @@ public class BatchOrderEventListener { private final BatchOrderStore batchOrderStore; private final BatchStore batchStore; private final IrsItemGraphQueryService irsItemGraphQueryService; + private final EssService essService; private final ApplicationEventPublisher applicationEventPublisher; private final TimeoutSchedulerBatchProcessingService timeoutScheduler; @@ -109,21 +112,32 @@ public void handleBatchProcessingFinishedEvent(final BatchProcessingFinishedEven } private void startBatch(final BatchOrder batchOrder, final Batch batch) { - // here we use only globalAssetId - final List createdJobIds = batch.getJobProgressList() - .stream() - .map(JobProgress::getIdentificationKey) - .map(identificationKey -> createRegisterJob(batchOrder, identificationKey)) - .map(registerJob -> createJobProgress( - irsItemGraphQueryService.registerItemJob(registerJob, - batch.getBatchId(), batch.getOwner()), - registerJob.getKey())) - .toList(); - batch.setJobProgressList(createdJobIds); + final List keyStream = batch.getJobProgressList() + .stream() + .map(JobProgress::getIdentificationKey) + .toList(); + if (batchOrder.getJobType().equals(BatchOrder.JobType.REGULAR)) { + batch.setJobProgressList(keyStream.stream() + .map(identificationKey -> createRegisterJob(batchOrder, identificationKey)) + .map(registerJob -> createJobProgress( + irsItemGraphQueryService.registerItemJob(registerJob, + batch.getBatchId(), batch.getOwner()), + registerJob.getKey())) + .toList()); + } else if (batchOrder.getJobType().equals(BatchOrder.JobType.ESS)) { + batch.setJobProgressList(keyStream.stream() + .map(identificationKey -> createRegisterBpnInvestigationBatchOrder( + batchOrder, identificationKey)) + .map(registerJob -> createJobProgress( + essService.startIrsJob(registerJob, batch.getBatchId(), + batch.getOwner()), registerJob.getKey())) + .toList()); + } + batch.setStartedOn(ZonedDateTime.now(ZoneOffset.UTC)); batchStore.save(batch.getBatchId(), batch); timeoutScheduler.registerBatchTimeout(batch.getBatchId(), batchOrder.getTimeout()); - timeoutScheduler.registerJobsTimeout(createdJobIds.stream().map(JobProgress::getJobId).toList(), + timeoutScheduler.registerJobsTimeout(batch.getJobProgressList().stream().map(JobProgress::getJobId).toList(), batchOrder.getJobTimeout()); } @@ -148,6 +162,15 @@ private RegisterJob createRegisterJob(final BatchOrder batchOrder, final PartCha .build(); } + private RegisterBpnInvestigationJob createRegisterBpnInvestigationBatchOrder(final BatchOrder batchOrder, final PartChainIdentificationKey identificationKey) { + return RegisterBpnInvestigationJob.builder() + .key(identificationKey) + .bomLifecycle(batchOrder.getBomLifecycle()) + .callbackUrl(batchOrder.getCallbackUrl()) + .incidentBPNSs(List.of()) + .build(); + } + private ProcessingState calculateBatchOrderState(final List stateList) { if (stateList.stream().anyMatch(ProcessingState.PROCESSING::equals)) { return ProcessingState.PROCESSING; diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/services/CreationBatchService.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/services/CreationBatchService.java index 56448ecf34..7a7df52b6f 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/services/CreationBatchService.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/services/CreationBatchService.java @@ -24,6 +24,7 @@ package org.eclipse.tractusx.irs.services; import java.util.List; +import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; @@ -34,6 +35,7 @@ import org.eclipse.tractusx.irs.common.auth.SecurityHelperService; import org.eclipse.tractusx.irs.component.PartChainIdentificationKey; import org.eclipse.tractusx.irs.component.RegisterBatchOrder; +import org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder; import org.eclipse.tractusx.irs.component.enums.JobState; import org.eclipse.tractusx.irs.component.enums.ProcessingState; import org.eclipse.tractusx.irs.configuration.IrsConfiguration; @@ -76,18 +78,42 @@ public UUID create(final RegisterBatchOrder request) { .jobTimeout(request.getJobTimeout()) .callbackUrl(request.getCallbackUrl()) .owner(securityHelperService.getClientIdClaim()) + .jobType(BatchOrder.JobType.REGULAR) .build(); - // need to use whole key - final List batches = createBatches(request.getKeys().stream().toList(), - request.getBatchSize(), batchOrderId, securityHelperService.getClientIdClaim()); - batchOrderStore.save(batchOrderId, batchOrder); + return createAndStore(request.getKeys(), request.getBatchSize(), batchOrder); + } + + public UUID create(final RegisterBpnInvestigationBatchOrder request) { + final UUID batchOrderId = UUID.randomUUID(); + final BatchOrder batchOrder = BatchOrder.builder() + .batchOrderId(batchOrderId) + .batchOrderState(ProcessingState.INITIALIZED) + .bomLifecycle(request.getBomLifecycle()) + .timeout(request.getTimeout()) + .jobTimeout(request.getJobTimeout()) + .callbackUrl(request.getCallbackUrl()) + .owner(securityHelperService.getClientIdClaim()) + .incidentBPNSs(request.getIncidentBPNSs()) + .jobType(BatchOrder.JobType.ESS) + .build(); + + return createAndStore(request.getKeys(), request.getBatchSize(), batchOrder); + } + + private UUID createAndStore(final Set keys, final int batchSize, final BatchOrder batchOrder) { + batchOrderStore.save(batchOrder.getBatchOrderId(), batchOrder); + + final List batches = createBatches(List.copyOf(keys), + batchSize, batchOrder.getBatchOrderId(), securityHelperService.getClientIdClaim()); batches.forEach(batch -> { batchStore.save(batch.getBatchId(), batch); jobEventLinkedQueueListener.addQueueForBatch(batch.getBatchId(), batch.getJobProgressList().size()); }); - applicationEventPublisher.publishEvent(new BatchOrderRegisteredEvent(batchOrderId)); - return batchOrderId; + + applicationEventPublisher.publishEvent(new BatchOrderRegisteredEvent(batchOrder.getBatchOrderId())); + + return batchOrder.getBatchOrderId(); } public List createBatches(final List keys, final int batchSize, final UUID batchOrderId, final String owner) { diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/services/IIrsItemGraphQueryService.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/services/IIrsItemGraphQueryService.java index 36740c6b76..89509d0d65 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/services/IIrsItemGraphQueryService.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/services/IIrsItemGraphQueryService.java @@ -43,7 +43,6 @@ public interface IIrsItemGraphQueryService { JobHandle registerItemJob(@NonNull RegisterJob request); PageResult getJobsByState(@NonNull List states, Pageable pageable); - PageResult getJobsByState(@NonNull List states, @NonNull List jobStates, Pageable pageable); Job cancelJobById(@NonNull UUID jobId); diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/services/IrsItemGraphQueryService.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/services/IrsItemGraphQueryService.java index 9a9865b9fe..e04a8fa644 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/services/IrsItemGraphQueryService.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/services/IrsItemGraphQueryService.java @@ -130,7 +130,7 @@ public IrsItemGraphQueryService(final JobOrchestrator states, final Pageable pageable) { + public PageResult getJobsByState(@NonNull final List states, final Pageable pageable) { final List jobs = filterJobs(states); final List jobStatusResults = jobs.stream() .map(job -> JobStatusResult.builder() @@ -155,12 +155,6 @@ private List filterJobs(final @NotNull List states) } } - @Override - public PageResult getJobsByState(@NonNull final List states, @NonNull final List jobStates, - final Pageable pageable) { - return getJobsByState(states.isEmpty() ? jobStates : states, pageable); - } - private PagedListHolder paginateAndSortResults(final Pageable pageable, final List results) { final PagedListHolder pageListHolder = new PagedListHolder<>(new ArrayList<>(results)); @@ -184,7 +178,7 @@ public JobHandle registerItemJob(final @NonNull RegisterJob request) { } public JobHandle registerItemJob(final @NonNull RegisterJob request, final UUID batchId, final String owner) { - final var params = buildJobParameter(request); + final var params = JobParameter.create(request); if (params.getDirection().equals(Direction.UPWARD) && !params.getBomLifecycle().equals(BomLifecycle.AS_BUILT)) { // Currently not supported variant throw new IllegalArgumentException("Upward direction is supported only for asBuilt bomLifecycle parameter!"); @@ -193,6 +187,7 @@ public JobHandle registerItemJob(final @NonNull RegisterJob request, final UUID throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Can't start job with BPN lookup - configured bpdm endpoint is empty!"); } + validateAspectTypeValues(params.getAspects()); final JobInitiateResponse jobInitiateResponse = orchestrator.startJob(request.getKey().getGlobalAssetId(), params, batchId, owner); @@ -206,33 +201,12 @@ public JobHandle registerItemJob(final @NonNull RegisterJob request, final UUID } } - private JobParameter buildJobParameter(final @NonNull RegisterJob request) { - final BomLifecycle bomLifecycle = Optional.ofNullable(request.getBomLifecycle()).orElse(BomLifecycle.AS_BUILT); - final List aspectTypeValues = Optional.ofNullable(request.getAspects()) - .orElse(List.of(bomLifecycle.getDefaultAspect())); - validateAspectTypeValues(aspectTypeValues); - final Direction direction = Optional.ofNullable(request.getDirection()).orElse(Direction.DOWNWARD); - - return JobParameter.builder() - .depth(request.getDepth()) - .bomLifecycle(bomLifecycle) - .bpn(request.getKey().getBpn()) - .direction(direction) - .aspects(aspectTypeValues.isEmpty() - ? List.of(bomLifecycle.getDefaultAspect()) - : aspectTypeValues) - .collectAspects(request.isCollectAspects()) - .lookupBPNs(request.isLookupBPNs()) - .callbackUrl(request.getCallbackUrl()) - .build(); - } - private void validateAspectTypeValues(final List aspectTypeValues) { try { final HashSet availableModels = new HashSet<>( semanticsHubFacade.getAllAspectModels().models()); - log.info("Available AspectModels: '{}'", availableModels); - log.info("Provided AspectModels: '{}'", aspectTypeValues); + log.debug("Number of available AspectModels: '{}'", availableModels.size()); + log.debug("Provided AspectModels: '{}'", aspectTypeValues); final Set availableNames = new HashSet<>(availableModels.stream().map(AspectModel::name).toList()); final Set availableUrns = new HashSet<>(availableModels.stream().map(AspectModel::urn).toList()); @@ -281,49 +255,53 @@ public Jobs getJobForJobId(final UUID jobId, final boolean includePartialResults throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Cannot access job with id " + jobId + " due to missing privileges."); } - final var relationships = new ArrayList(); - final var tombstones = new ArrayList(); - final var shells = new ArrayList(); - final var submodels = new ArrayList(); - final var bpns = new ArrayList(); - - if (jobIsCompleted(multiJob)) { - final var container = retrieveJobResultRelationships(multiJob.getJob().getId()); - relationships.addAll(container.getRelationships()); - tombstones.addAll(container.getTombstones()); - shells.addAll(container.getShells()); - submodels.addAll(container.getSubmodels()); - bpns.addAll(container.getBpns()); - } else if (includePartialResults) { - final var container = retrievePartialResults(multiJob); - relationships.addAll(container.getRelationships()); - tombstones.addAll(container.getTombstones()); - shells.addAll(container.getShells()); - submodels.addAll(container.getSubmodels()); - bpns.addAll(container.getBpns()); - } - - log.info("Found job with id {} in status {} with {} relationships, {} tombstones and {} submodels", jobId, - multiJob.getJob().getState(), relationships.size(), tombstones.size(), submodels.size()); - - return Jobs.builder() - .job(multiJob.getJob() - .toBuilder() - .summary(buildSummary(multiJob.getCompletedTransfers().size(), - multiJob.getTransferProcessIds().size(), tombstones.size(), - retrievePartialResults(multiJob))) - .build()) - .relationships(relationships) - .tombstones(tombstones) - .shells(shells) - .submodels(submodels) - .bpns(bpns) - .build(); + return getJobForJobId(multiJob, includePartialResults); } else { throw new ResponseStatusException(HttpStatus.NOT_FOUND, "No job exists with id " + jobId); } } + public Jobs getJobForJobId(final MultiTransferJob multiJob, final boolean includePartialResults) { + final var relationships = new ArrayList(); + final var tombstones = new ArrayList(); + final var shells = new ArrayList(); + final var submodels = new ArrayList(); + final var bpns = new ArrayList(); + + if (multiJob.jobIsCompleted()) { + final var container = retrieveJobResultRelationships(multiJob.getJob().getId()); + relationships.addAll(container.getRelationships()); + tombstones.addAll(container.getTombstones()); + shells.addAll(container.getShells()); + submodels.addAll(container.getSubmodels()); + bpns.addAll(container.getBpns()); + } else if (includePartialResults) { + final var container = retrievePartialResults(multiJob); + relationships.addAll(container.getRelationships()); + tombstones.addAll(container.getTombstones()); + shells.addAll(container.getShells()); + submodels.addAll(container.getSubmodels()); + bpns.addAll(container.getBpns()); + } + + log.info("Found job with id {} in status {} with {} relationships, {} tombstones and {} submodels", multiJob.getJob().getId(), + multiJob.getJob().getState(), relationships.size(), tombstones.size(), submodels.size()); + + return Jobs.builder() + .job(multiJob.getJob() + .toBuilder() + .summary(buildSummary(multiJob.getCompletedTransfers().size(), + multiJob.getTransferProcessIds().size(), tombstones.size(), + retrievePartialResults(multiJob))) + .build()) + .relationships(relationships) + .tombstones(tombstones) + .shells(shells) + .submodels(submodels) + .bpns(bpns) + .build(); + } + @Scheduled(cron = "${irs.job.jobstore.cron.expression}") public void updateJobsInJobStoreMetrics() { final List jobs = jobStore.findAll(); @@ -424,8 +402,4 @@ private ItemContainer retrieveJobResultRelationships(final UUID jobId) { } } - private boolean jobIsCompleted(final MultiTransferJob multiJob) { - return multiJob.getJob().getState().equals(JobState.COMPLETED); - } - } diff --git a/irs-api/src/main/resources/application.yml b/irs-api/src/main/resources/application.yml index 4bde83d0b7..476527e7c7 100644 --- a/irs-api/src/main/resources/application.yml +++ b/irs-api/src/main/resources/application.yml @@ -229,4 +229,10 @@ ess: mockEdcResult: { } # Mocked BPN Investigation results mockRecursiveEdcAsset: # Mocked BPN Recursive Investigation results -apiAllowedBpn: ${API_ALLOWED_BPN:BPNL00000003CRHK} # BPN value that is allowed to access IRS API +apiAllowedBpn: ${API_ALLOWED_BPN:BPNL00000001CRHK} # BPN value that is allowed to access IRS API + +# OAuth2 JWT token parse config. This configures the structure IRS expects when parsing the IRS role of an access token. +oauth: + resourceClaim: "resource_access" # Name of the JWT claim for roles + irsNamespace: "Cl20-CX-IRS" # Namespace for the IRS roles + roles: "roles" # Name of the list of roles within the IRS namespace diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsFunctionalTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsFunctionalTest.java index ae66b18fc7..0d72e54a19 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsFunctionalTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsFunctionalTest.java @@ -190,7 +190,7 @@ private void thereIsJwtAuthentication() { Jwt jwt() { return new Jwt("token", Instant.now(), Instant.now().plusSeconds(30), Map.of("alg", "none"), - Map.of(SUB, "sub", "clientId", "clientId", "bpn", "BPNL00000003CRHK")); + Map.of(SUB, "sub", "clientId", "clientId", "bpn", "BPNL00000001CRHK")); } @NotNull diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/configuration/converter/JwtAuthenticationConverterTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/configuration/converter/JwtAuthenticationConverterTest.java index 1dd0fb96d1..b79822fce1 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/configuration/converter/JwtAuthenticationConverterTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/configuration/converter/JwtAuthenticationConverterTest.java @@ -23,9 +23,8 @@ ********************************************************************************/ package org.eclipse.tractusx.irs.configuration.converter; -import static org.springframework.security.oauth2.jwt.JwtClaimNames.SUB; - import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.security.oauth2.jwt.JwtClaimNames.SUB; import java.time.Instant; import java.util.List; @@ -33,14 +32,25 @@ import com.nimbusds.jose.shaded.gson.internal.LinkedTreeMap; import org.eclipse.tractusx.irs.common.auth.IrsRoles; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; class JwtAuthenticationConverterTest { - private final JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter(); + private JwtAuthenticationConverter jwtAuthenticationConverter; + + @BeforeEach + void setUp() { + final String resourceAccessClaim = "resource_access"; + final String irsResourceAccess = "Cl20-CX-IRS"; + final String roles = "roles"; + jwtAuthenticationConverter = new JwtAuthenticationConverter(new JwtGrantedAuthoritiesConverter(), + new IrsTokenParser(resourceAccessClaim, irsResourceAccess, roles)); + } @Test void shouldParseJwtTokenAndFindViewIrsRole() { @@ -98,9 +108,8 @@ void shouldParseJwtTokenAndNotFindIrsRolesWhenWrongRolesKey() { Jwt jwt(final Map irsResourceAccess) { final Map claims = new LinkedTreeMap<>(); - claims.putAll(Map.of("resource_access", irsResourceAccess, SUB, "sub", "clientId", "clientId")); + claims.putAll(Map.of("resource_access", irsResourceAccess, SUB, "sub", "clientId", "clientId")); - return new Jwt("token", Instant.now(), Instant.now().plusSeconds(30), Map.of("alg", "none"), - claims); + return new Jwt("token", Instant.now(), Instant.now().plusSeconds(30), Map.of("alg", "none"), claims); } } \ No newline at end of file diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/BatchControllerTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/BatchControllerTest.java index f5e9e0b8e1..2e1b5cef34 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/BatchControllerTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/BatchControllerTest.java @@ -24,6 +24,7 @@ package org.eclipse.tractusx.irs.controllers; import static org.eclipse.tractusx.irs.util.TestMother.registerBatchOrder; +import static org.eclipse.tractusx.irs.util.TestMother.registerBpnInvestigationBatchOrder; import static org.hamcrest.Matchers.containsString; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -105,7 +106,7 @@ void shouldReturnBadRequestWhenBatchSizeNotMod10Compliant() throws Exception { @Test @WithMockUser(authorities = IrsRoles.VIEW_IRS) - void shouldRegisterBatchOrder() throws Exception { + void shouldRegisterRegularJobBatchOrder() throws Exception { when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE); this.mockMvc.perform(post("/irs/orders").contentType(MediaType.APPLICATION_JSON) @@ -114,6 +115,17 @@ void shouldRegisterBatchOrder() throws Exception { .andExpect(status().isCreated()); } + @Test + @WithMockUser(authorities = IrsRoles.VIEW_IRS) + void shouldRegisterEssJobBatchOrder() throws Exception { + when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE); + + this.mockMvc.perform(post("/irs/ess/orders").contentType(MediaType.APPLICATION_JSON) + .content(new ObjectMapper().writeValueAsString( + registerBpnInvestigationBatchOrder("urn:uuid:4132cd2b-cbe7-4881-a6b4-39fdc31cca2b")))) + .andExpect(status().isCreated()); + } + @Test @WithMockUser(authorities = IrsRoles.VIEW_IRS) void shouldReturnBatchOrder() throws Exception { diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/IrsControllerTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/IrsControllerTest.java index 6d4f5543ed..21618ef0db 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/IrsControllerTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/controllers/IrsControllerTest.java @@ -185,7 +185,7 @@ void getJobsByState() throws Exception { final String returnJobAsString = objectMapper.writeValueAsString(returnedJob); when(authorizationService.verifyBpn()).thenReturn(Boolean.TRUE); - when(service.getJobsByState(any(), any(), any())).thenReturn( + when(service.getJobsByState(any(), any())).thenReturn( new PageResult(new PagedListHolder<>(List.of(returnedJob)))); this.mockMvc.perform(get("/irs/jobs")) diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/service/EssServiceTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/service/EssServiceTest.java index 5610665e6f..bf717b44a2 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/service/EssServiceTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/service/EssServiceTest.java @@ -27,12 +27,14 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.UUID; import org.eclipse.tractusx.irs.common.auth.IrsRoles; @@ -45,6 +47,8 @@ import org.eclipse.tractusx.irs.component.RegisterBpnInvestigationJob; import org.eclipse.tractusx.irs.component.RegisterJob; import org.eclipse.tractusx.irs.component.enums.JobState; +import org.eclipse.tractusx.irs.connector.job.JobStore; +import org.eclipse.tractusx.irs.connector.job.MultiTransferJob; import org.eclipse.tractusx.irs.edc.client.model.notification.EdcNotification; import org.eclipse.tractusx.irs.edc.client.model.notification.EdcNotificationHeader; import org.eclipse.tractusx.irs.edc.client.model.notification.ResponseNotificationContent; @@ -60,10 +64,11 @@ class EssServiceTest { private final SecurityHelperService securityHelperService = mock(SecurityHelperService.class); private final BpnInvestigationJobCache bpnInvestigationJobCache = new InMemoryBpnInvestigationJobCache(); + private final JobStore jobStore = mock(JobStore.class); private final EssRecursiveNotificationHandler recursiveNotificationHandler = Mockito.mock( EssRecursiveNotificationHandler.class); private final EssService essService = new EssService(irsItemGraphQueryService, securityHelperService, bpnInvestigationJobCache, - recursiveNotificationHandler); + jobStore, recursiveNotificationHandler); @Test void shouldSuccessfullyStartJobAndReturnWithExtendedSubmodelList() { @@ -89,8 +94,9 @@ void shouldSuccessfullyStartJobAndReturnWithExtendedSubmodelList() { .shells(new ArrayList<>()) .build(); - when(irsItemGraphQueryService.registerItemJob(any(RegisterJob.class))).thenReturn(JobHandle.builder().id(createdJobId).build()); - when(irsItemGraphQueryService.getJobForJobId(createdJobId, true)).thenReturn(expectedResponse); + when(irsItemGraphQueryService.registerItemJob(any(RegisterJob.class), any(), any())).thenReturn(JobHandle.builder().id(createdJobId).build()); + when(jobStore.find(createdJobId.toString())).thenReturn(Optional.of(MultiTransferJob.builder().job(expectedResponse.getJob()).build())); + when(irsItemGraphQueryService.getJobForJobId(any(MultiTransferJob.class), eq(true))).thenReturn(expectedResponse); when(securityHelperService.isAdmin()).thenReturn(true); final JobHandle jobHandle = essService.startIrsJob(request); @@ -129,7 +135,7 @@ void shouldUpdateJobSnapshotIfNotificationFound() { .build(); final BpnInvestigationJob bpnInvestigationJob = BpnInvestigationJob.create( - Jobs.builder().job(Job.builder().id(jobId).owner(owner).build()).build(), owner, new ArrayList<>()) + Jobs.builder().job(Job.builder().id(jobId).owner(owner).build()).build(), new ArrayList<>()) .withNotifications(List.of(notificationId, notificationId2)); bpnInvestigationJobCache.store(jobId, bpnInvestigationJob); @@ -167,8 +173,9 @@ void shouldKeepJobInRunningIfNotificationIsOpen() { final String owner = securityHelperService.getClientIdClaim(); when(securityHelperService.isAdmin()).thenReturn(true); - final BpnInvestigationJob bpnInvestigationJob = BpnInvestigationJob.create(job(jobId, owner), owner, - new ArrayList<>()).withNotifications(Collections.singletonList(notificationId)); + final BpnInvestigationJob bpnInvestigationJob = BpnInvestigationJob.create(Jobs.builder().job(Job.builder().id(jobId).owner(owner).build()).build(), + new ArrayList<>()) + .withNotifications(Collections.singletonList(notificationId)); bpnInvestigationJobCache.store(jobId, bpnInvestigationJob); assertThat(bpnInvestigationJobCache.findAll()).hasSize(1); @@ -192,7 +199,7 @@ void shouldCompleteJobIfAllNotificationsSentWereAnswered() { final UUID jobId = UUID.randomUUID(); final Jobs jobSnapshot = job(jobId, owner); - final BpnInvestigationJob bpnInvestigationJob = BpnInvestigationJob.create(jobSnapshot, owner, null) + final BpnInvestigationJob bpnInvestigationJob = BpnInvestigationJob.create(jobSnapshot, null) .withAnsweredNotification(notificationId) .withNotifications(List.of()); bpnInvestigationJobCache.store(jobId, bpnInvestigationJob); @@ -214,7 +221,7 @@ void shouldCompleteJobIfFinalNotificationWasReceived() { final UUID jobId = UUID.randomUUID(); final Jobs jobSnapshot = job(jobId, owner); final String notificationId = UUID.randomUUID().toString(); - final BpnInvestigationJob bpnInvestigationJob = BpnInvestigationJob.create(jobSnapshot, owner, null) + final BpnInvestigationJob bpnInvestigationJob = BpnInvestigationJob.create(jobSnapshot, null) .update(jobSnapshot, SupplyChainImpacted.NO) .withNotifications(List.of(notificationId)); bpnInvestigationJobCache.store(jobId, bpnInvestigationJob); diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/service/InvestigationJobProcessingEventListenerTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/service/InvestigationJobProcessingEventListenerTest.java index 83ae37291b..28cee66ddd 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/service/InvestigationJobProcessingEventListenerTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/ess/service/InvestigationJobProcessingEventListenerTest.java @@ -49,6 +49,8 @@ import org.eclipse.tractusx.irs.component.assetadministrationshell.AssetAdministrationShellDescriptor; import org.eclipse.tractusx.irs.component.assetadministrationshell.IdentifierKeyValuePair; import org.eclipse.tractusx.irs.component.enums.JobState; +import org.eclipse.tractusx.irs.connector.job.JobStore; +import org.eclipse.tractusx.irs.connector.job.MultiTransferJob; import org.eclipse.tractusx.irs.data.StringMapper; import org.eclipse.tractusx.irs.edc.client.EdcSubmodelFacade; import org.eclipse.tractusx.irs.edc.client.exceptions.EdcClientException; @@ -71,6 +73,7 @@ class InvestigationJobProcessingEventListenerTest { private final IrsItemGraphQueryService irsItemGraphQueryService = mock(IrsItemGraphQueryService.class); private final EdcSubmodelFacade edcSubmodelFacade = mock(EdcSubmodelFacade.class); private final BpnInvestigationJobCache bpnInvestigationJobCache = Mockito.mock(BpnInvestigationJobCache.class); + private final JobStore jobStore = Mockito.mock(JobStore.class); private final EssRecursiveNotificationHandler recursiveNotificationHandler = Mockito.mock( EssRecursiveNotificationHandler.class); private final UUID jobId = UUID.randomUUID(); @@ -79,8 +82,8 @@ class InvestigationJobProcessingEventListenerTest { private final ConnectorEndpointsService connectorEndpointsService = mock(ConnectorEndpointsService.class); private final InvestigationJobProcessingEventListener jobProcessingEventListener = new InvestigationJobProcessingEventListener( - irsItemGraphQueryService, connectorEndpointsService, edcSubmodelFacade, bpnInvestigationJobCache, "", "", List.of(), - recursiveNotificationHandler); + irsItemGraphQueryService, connectorEndpointsService, edcSubmodelFacade, bpnInvestigationJobCache, jobStore, + "", "", List.of(), recursiveNotificationHandler); @Captor ArgumentCaptor> edcNotificationCaptor; @@ -346,10 +349,11 @@ private void createMockForJobIdAndShell(final UUID mockedJobId, final String moc .shells(List.of(createShell(UUID.randomUUID().toString(), mockedShell))) .submodels(List.of(partAsPlanned, partSiteInformationAsPlanned)) .build(); - final BpnInvestigationJob bpnInvestigationJob = BpnInvestigationJob.create(jobs, "owner", incindentBPNSs); + final BpnInvestigationJob bpnInvestigationJob = BpnInvestigationJob.create(jobs, incindentBPNSs); when(bpnInvestigationJobCache.findByJobId(mockedJobId)).thenReturn(Optional.of(bpnInvestigationJob)); - when(irsItemGraphQueryService.getJobForJobId(mockedJobId, false)).thenReturn(jobs); + when(jobStore.find(mockedJobId.toString())).thenReturn(Optional.of(MultiTransferJob.builder().job(jobs.getJob()).build())); + when(irsItemGraphQueryService.getJobForJobId(any(MultiTransferJob.class), eq(false))).thenReturn(jobs); } private void createMockForJobIdAndShell(final UUID mockedJobId, final String mockedShell, @@ -365,11 +369,12 @@ private void createMockForJobIdAndShells(final UUID mockedJobId, final List createShell(UUID.randomUUID().toString(), bpn)).toList()) .build(); - final BpnInvestigationJob bpnInvestigationJob = BpnInvestigationJob.create(jobs, "owner", + final BpnInvestigationJob bpnInvestigationJob = BpnInvestigationJob.create(jobs, List.of("BPNS000000000DDD")); when(bpnInvestigationJobCache.findByJobId(mockedJobId)).thenReturn(Optional.of(bpnInvestigationJob)); - when(irsItemGraphQueryService.getJobForJobId(mockedJobId, false)).thenReturn(jobs); + when(jobStore.find(mockedJobId.toString())).thenReturn(Optional.of(MultiTransferJob.builder().job(jobs.getJob()).build())); + when(irsItemGraphQueryService.getJobForJobId(any(MultiTransferJob.class), eq(false))).thenReturn(jobs); } } diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/services/BatchOrderEventListenerTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/services/BatchOrderEventListenerTest.java index e15892de91..06134f8dd6 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/services/BatchOrderEventListenerTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/services/BatchOrderEventListenerTest.java @@ -46,6 +46,7 @@ import org.eclipse.tractusx.irs.connector.batch.InMemoryBatchOrderStore; import org.eclipse.tractusx.irs.connector.batch.InMemoryBatchStore; import org.eclipse.tractusx.irs.connector.batch.JobProgress; +import org.eclipse.tractusx.irs.ess.service.EssService; import org.eclipse.tractusx.irs.services.events.BatchOrderProcessingFinishedEvent; import org.eclipse.tractusx.irs.services.events.BatchOrderRegisteredEvent; import org.eclipse.tractusx.irs.services.events.BatchProcessingFinishedEvent; @@ -62,6 +63,7 @@ class BatchOrderEventListenerTest { private BatchOrderStore batchOrderStore; private BatchStore batchStore; private final IrsItemGraphQueryService irsItemGraphQueryService = mock(IrsItemGraphQueryService.class); + private final EssService essService = mock(EssService.class); private final ApplicationEventPublisher applicationEventPublisher = mock(ApplicationEventPublisher.class); private final TimeoutSchedulerBatchProcessingService timeoutScheduler = mock( TimeoutSchedulerBatchProcessingService.class); @@ -73,7 +75,7 @@ void beforeEach() { batchOrderStore = new InMemoryBatchOrderStore(); batchStore = new InMemoryBatchStore(); eventListener = new BatchOrderEventListener(batchOrderStore, batchStore, irsItemGraphQueryService, - applicationEventPublisher, timeoutScheduler); + essService, applicationEventPublisher, timeoutScheduler); } @Test @@ -90,6 +92,7 @@ void shouldStartFirstBatch() { .jobTimeout(timeout) .lookupBPNs(Boolean.TRUE) .owner(owner) + .jobType(BatchOrder.JobType.REGULAR) .build(); final Batch firstBatch = Batch.builder() .batchId(FIRST_BATCH_ID) @@ -135,6 +138,7 @@ void shouldStartNextBatchWhenPreviousFinished() { .jobTimeout(timeout) .lookupBPNs(Boolean.TRUE) .owner(owner) + .jobType(BatchOrder.JobType.ESS) .build(); final Batch firstBatch = Batch.builder() .batchId(FIRST_BATCH_ID) @@ -152,7 +156,7 @@ void shouldStartNextBatchWhenPreviousFinished() { .owner(owner) .build(); - given(irsItemGraphQueryService.registerItemJob(any(), any(), eq(owner))).willReturn( + given(essService.startIrsJob(any(), any(), eq(owner))).willReturn( JobHandle.builder().id(UUID.randomUUID()).build()); batchOrderStore.save(BATCH_ORDER_ID, batchOrder); @@ -161,7 +165,7 @@ void shouldStartNextBatchWhenPreviousFinished() { // when eventListener.handleBatchProcessingFinishedEvent(new BatchProcessingFinishedEvent(BATCH_ORDER_ID, FIRST_BATCH_ID, ProcessingState.PARTIAL, ProcessingState.COMPLETED, 1, "")); // then - verify(irsItemGraphQueryService, times(numberOfJobs)).registerItemJob(any(), eq(SECOND_BATCH_ID), eq(owner)); + verify(essService, times(numberOfJobs)).startIrsJob(any(), eq(SECOND_BATCH_ID), eq(owner)); verify(timeoutScheduler, times(1)).registerBatchTimeout(SECOND_BATCH_ID, timeout); verify(timeoutScheduler, times(1)).registerJobsTimeout(anyList(), eq(timeout)); } @@ -177,6 +181,7 @@ void shouldPublishBatchOrderProcessingFinishedEventWhenAllBatchesCompleted() { .timeout(timeout) .jobTimeout(timeout) .lookupBPNs(Boolean.TRUE) + .jobType(BatchOrder.JobType.REGULAR) .build(); final Batch firstBatch = Batch.builder() .batchId(FIRST_BATCH_ID) diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/services/CreationBatchServiceTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/services/CreationBatchServiceTest.java index 6a1eda842a..3bb1842974 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/services/CreationBatchServiceTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/services/CreationBatchServiceTest.java @@ -39,6 +39,7 @@ import org.eclipse.tractusx.irs.common.auth.SecurityHelperService; import org.eclipse.tractusx.irs.component.PartChainIdentificationKey; import org.eclipse.tractusx.irs.component.RegisterBatchOrder; +import org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder; import org.eclipse.tractusx.irs.component.enums.BatchStrategy; import org.eclipse.tractusx.irs.component.enums.BomLifecycle; import org.eclipse.tractusx.irs.component.enums.Direction; @@ -75,7 +76,7 @@ void beforeEach() { } @Test - void shouldStoreBatchOrder() throws MalformedURLException { + void shouldStoreRegularBatchOrder() throws MalformedURLException { // given final RegisterBatchOrder registerBatchOrder = exampleBatchRequest(); given(irsConfiguration.getApiUrl()).willReturn(new URL(EXAMPLE_URL)); @@ -94,6 +95,26 @@ void shouldStoreBatchOrder() throws MalformedURLException { Collectors.toList())).containsOnly(FIRST_GLOBAL_ASSET_ID, SECOND_GLOBAL_ASSET_ID); } + @Test + void shouldStoreESSBatchOrder() throws MalformedURLException { + // given + final RegisterBpnInvestigationBatchOrder registerBatchOrder = exampleESSBatchRequest(); + given(irsConfiguration.getApiUrl()).willReturn(new URL(EXAMPLE_URL)); + + // when + final UUID batchOrderId = service.create(registerBatchOrder); + + // then + assertThat(batchOrderId).isNotNull(); + assertThat(batchOrderStore.findAll()).hasSize(1); + assertThat(batchStore.findAll()).hasSize(1); + + Batch actual = batchStore.findAll().stream().findFirst().orElseThrow(); + assertThat(actual.getJobProgressList().stream().map(JobProgress::getIdentificationKey).map( + PartChainIdentificationKey::getGlobalAssetId).collect( + Collectors.toList())).containsOnly(FIRST_GLOBAL_ASSET_ID, SECOND_GLOBAL_ASSET_ID); + } + @Test void shouldSplitIdentificationKeysIdIntoBatches() throws MalformedURLException { // given @@ -138,4 +159,17 @@ private static RegisterBatchOrder exampleBatchRequest() { .build(); } + private static RegisterBpnInvestigationBatchOrder exampleESSBatchRequest() { + return RegisterBpnInvestigationBatchOrder.builder() + .keys(Set.of(PartChainIdentificationKey.builder().globalAssetId(FIRST_GLOBAL_ASSET_ID).build(), + PartChainIdentificationKey.builder().globalAssetId(SECOND_GLOBAL_ASSET_ID).build())) + .bomLifecycle(BomLifecycle.AS_PLANNED) + .timeout(1000) + .jobTimeout(500) + .batchStrategy(BatchStrategy.PRESERVE_JOB_ORDER) + .callbackUrl(EXAMPLE_URL) + .batchSize(10) + .build(); + } + } \ No newline at end of file diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/services/IrsItemGraphQueryServiceTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/services/IrsItemGraphQueryServiceTest.java index 7e74d01a1c..9f015bf94d 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/services/IrsItemGraphQueryServiceTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/services/IrsItemGraphQueryServiceTest.java @@ -191,46 +191,16 @@ void shouldReturnFoundJobs() { assertThat(jobs.totalElements()).isEqualTo(1); } - @Test - void shouldTakeStatesInsteadOfDeprecatedParameter() { - final List states = List.of(JobState.COMPLETED); - final List jobStates = List.of(JobState.RUNNING); - - testee.getJobsByState(states, jobStates, Pageable.ofSize(10)); - - verify(jobStore).findByStates(states); - } - - @Test - void shouldTakeDeprecatedParameterWhenCorrectOneIsEmpty() { - final List states = List.of(); - final List jobStates = List.of(JobState.RUNNING); - - testee.getJobsByState(states, jobStates, Pageable.ofSize(10)); - - verify(jobStore).findByStates(jobStates); - } - @Test void shouldTakeAllJobsWhenBothListEmpty() { final List states = List.of(); final List jobStates = List.of(); - testee.getJobsByState(states, jobStates, Pageable.ofSize(10)); + testee.getJobsByState(states, Pageable.ofSize(10)); verify(jobStore).findAll(); } - @Test - void shouldTakeStatesInsteadOfEmptyDeprecatedParameter() { - final List states = List.of(JobState.COMPLETED); - final List jobStates = List.of(); - - testee.getJobsByState(states, jobStates, Pageable.ofSize(10)); - - verify(jobStore).findByStates(states); - } - @Test void shouldThrowExceptionForInvalidAspectTypes() throws SchemaNotFoundException { when(semanticsHubFacade.getAllAspectModels()).thenReturn(AspectModels.builder().models(List.of()).build()); @@ -258,8 +228,6 @@ void shouldThrowExceptionForUnknownAspectTypes() throws SchemaNotFoundException @Test void shouldThrowExceptionWhenLookupBpnAndBpdmEndpointUrlIsMissing() throws SchemaNotFoundException { - when(semanticsHubFacade.getAllAspectModels()).thenReturn(AspectModels.builder().models(List.of(AspectModel.builder().name("SingleLevelBomAsBuilt").build())).build()); - final Executable executable = () -> testee.registerItemJob(registerJobWithLookupBPNs()); assertThrows(ResponseStatusException.class, executable); diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/util/TestMother.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/util/TestMother.java index 9d5521c1a5..52a77fb820 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/util/TestMother.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/util/TestMother.java @@ -41,6 +41,7 @@ import org.eclipse.tractusx.irs.component.LinkedItem; import org.eclipse.tractusx.irs.component.PartChainIdentificationKey; import org.eclipse.tractusx.irs.component.RegisterBatchOrder; +import org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder; import org.eclipse.tractusx.irs.component.RegisterJob; import org.eclipse.tractusx.irs.component.Relationship; import org.eclipse.tractusx.irs.component.assetadministrationshell.AssetAdministrationShellDescriptor; @@ -122,11 +123,11 @@ public static RegisterJob registerJob(final String globalAssetId, final Integer return registerJob; } - public static RegisterBatchOrder registerBatchOrder(final String... globalAssetId) { + public static RegisterBatchOrder registerBatchOrder(final String... globalAssetIds) { final RegisterBatchOrder registerBatchOrder = new RegisterBatchOrder(); - registerBatchOrder.setKeys(Arrays.stream(globalAssetId) - .map(x -> PartChainIdentificationKey.builder() - .globalAssetId(x) + registerBatchOrder.setKeys(Arrays.stream(globalAssetIds) + .map(globalAssetId -> PartChainIdentificationKey.builder() + .globalAssetId(globalAssetId) .bpn("BPNL0000000001AA") .build()) .collect(Collectors.toSet())); @@ -134,6 +135,18 @@ public static RegisterBatchOrder registerBatchOrder(final String... globalAssetI return registerBatchOrder; } + public static RegisterBpnInvestigationBatchOrder registerBpnInvestigationBatchOrder(final String... globalAssetIds) { + final RegisterBpnInvestigationBatchOrder registerBpnInvestigationBatchOrder = new RegisterBpnInvestigationBatchOrder(); + registerBpnInvestigationBatchOrder.setKeys(Arrays.stream(globalAssetIds) + .map(globalAssetId -> PartChainIdentificationKey.builder() + .globalAssetId(globalAssetId) + .bpn("BPNL0000000001AA") + .build()) + .collect(Collectors.toSet())); + registerBpnInvestigationBatchOrder.setIncidentBPNSs(List.of("BPNL0000000002BB")); + return registerBpnInvestigationBatchOrder; + } + public static JobParameter jobParameter() { return JobParameter.builder() .depth(0) diff --git a/irs-common/pom.xml b/irs-common/pom.xml index 2c9ea7fe49..1473cbe828 100644 --- a/irs-common/pom.xml +++ b/irs-common/pom.xml @@ -60,17 +60,10 @@ io.minio minio - ${minio.version} - - - com.fasterxml.jackson.core - jackson-databind - - - org.bouncycastle - bcprov-jdk15on - - + + + org.xerial.snappy + snappy-java com.squareup.okio diff --git a/irs-common/src/test/java/org/eclipse/tractusx/irs/common/auth/AuthorizationServiceTest.java b/irs-common/src/test/java/org/eclipse/tractusx/irs/common/auth/AuthorizationServiceTest.java index 987ef66eef..1cc89800f7 100644 --- a/irs-common/src/test/java/org/eclipse/tractusx/irs/common/auth/AuthorizationServiceTest.java +++ b/irs-common/src/test/java/org/eclipse/tractusx/irs/common/auth/AuthorizationServiceTest.java @@ -42,7 +42,7 @@ class AuthorizationServiceTest { @Test void shouldReturnTrueWhenTokenBpnIsEqualToAllowedBpn() { // given - final String BPN = "BPNL00000003CRHK"; + final String BPN = "BPNL00000001CRHK"; final Map claims = Map.of(SUB, "sub", "clientId", "clientId", "bpn", BPN); thereIsJwtAuthenticationWithClaims(claims); final AuthorizationService authorizationService = new AuthorizationService(BPN); diff --git a/irs-common/src/test/java/org/eclipse/tractusx/irs/common/auth/SecurityHelperServiceTest.java b/irs-common/src/test/java/org/eclipse/tractusx/irs/common/auth/SecurityHelperServiceTest.java index 33a7e9b9d0..1d2cd0b105 100644 --- a/irs-common/src/test/java/org/eclipse/tractusx/irs/common/auth/SecurityHelperServiceTest.java +++ b/irs-common/src/test/java/org/eclipse/tractusx/irs/common/auth/SecurityHelperServiceTest.java @@ -43,7 +43,7 @@ class SecurityHelperServiceTest { private final String CLIENT_ID = "sa-cl6-cx-2"; - private final String BPN = "BPNL00000003CRHK"; + private final String BPN = "BPNL00000001CRHK"; final SecurityHelperService securityHelperService = new SecurityHelperService(); diff --git a/irs-edc-client/pom.xml b/irs-edc-client/pom.xml index 665605bcd7..640da1a559 100644 --- a/irs-edc-client/pom.xml +++ b/irs-edc-client/pom.xml @@ -133,23 +133,14 @@ org.eclipse.edc - jetty-xml - org.eclipse.jetty - - - jetty-http - org.eclipse.jetty + websocket-jakarta-server + org.eclipse.jetty.websocket - jetty-http - org.eclipse.jetty - 11.0.16 - - - jetty-xml - org.eclipse.jetty + org.eclipse.jetty.websocket + websocket-jakarta-server 11.0.16 diff --git a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/JobParameter.java b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/JobParameter.java index 0dd02fae16..7ceaf00c72 100644 --- a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/JobParameter.java +++ b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/JobParameter.java @@ -24,13 +24,14 @@ package org.eclipse.tractusx.irs.component; import java.util.List; - +import java.util.Optional; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import lombok.AllArgsConstructor; import lombok.Builder; +import lombok.NonNull; import lombok.Singular; import lombok.Value; import lombok.extern.jackson.Jacksonized; @@ -73,4 +74,24 @@ public class JobParameter { private String callbackUrl; + public static JobParameter create(final @NonNull RegisterJob request) { + final BomLifecycle bomLifecycle = Optional.ofNullable(request.getBomLifecycle()).orElse(BomLifecycle.AS_BUILT); + final List aspectTypeValues = Optional.ofNullable(request.getAspects()) + .orElse(List.of(bomLifecycle.getDefaultAspect())); + final Direction direction = Optional.ofNullable(request.getDirection()).orElse(Direction.DOWNWARD); + + return JobParameter.builder() + .depth(request.getDepth()) + .bomLifecycle(bomLifecycle) + .bpn(request.getKey().getBpn()) + .direction(direction) + .aspects(aspectTypeValues.isEmpty() + ? List.of(bomLifecycle.getDefaultAspect()) + : aspectTypeValues) + .collectAspects(request.isCollectAspects()) + .lookupBPNs(request.isLookupBPNs()) + .callbackUrl(request.getCallbackUrl()) + .build(); + } + } diff --git a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/RegisterBpnInvestigationBatchOrder.java b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/RegisterBpnInvestigationBatchOrder.java new file mode 100644 index 0000000000..c7b62b01da --- /dev/null +++ b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/RegisterBpnInvestigationBatchOrder.java @@ -0,0 +1,146 @@ +/******************************************************************************** + * Copyright (c) 2021,2022,2023 + * 2022: ZF Friedrichshafen AG + * 2022: ISTOS GmbH + * 2022,2023: Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * 2022,2023: BOSCH AG + * Copyright (c) 2021,2022,2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.component; + +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.DEFAULT_BATCH_SIZE; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.DEFAULT_BATCH_SIZE_DESC; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.DEFAULT_JOB_TIMEOUT; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.DEFAULT_JOB_TIMEOUT_DESC; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.DEFAULT_TIMEOUT; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.DEFAULT_TIMEOUT_DESC; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.MAX_BATCH_SIZE; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.MAX_BATCH_SIZE_DESC; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.MAX_JOB_TIMEOUT; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.MAX_JOB_TIMEOUT_DESC; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.MAX_TIMEOUT; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.MAX_TIMEOUT_DESC; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.MIN_BATCH_SIZE; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.MIN_BATCH_SIZE_DESC; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.MIN_JOB_TIMEOUT; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.MIN_JOB_TIMEOUT_DESC; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.MIN_TIMEOUT; +import static org.eclipse.tractusx.irs.component.RegisterBpnInvestigationBatchOrder.RegisterBatchOrderConstants.MIN_TIMEOUT_DESC; + +import java.util.List; +import java.util.Set; + +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.eclipse.tractusx.irs.component.enums.BatchStrategy; +import org.eclipse.tractusx.irs.component.enums.BomLifecycle; +import org.eclipse.tractusx.irs.validators.Mod10; +import org.hibernate.validator.constraints.URL; + +/** + * Request body for registering a new Batch Order for ESS Investigation Job + */ +@Schema(description = "Request body for registering a new Batch Order for ESS Investigation Job.") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@SuppressWarnings({ "PMD.TooManyStaticImports", + "PMD.ExcessiveImports", +}) +public class RegisterBpnInvestigationBatchOrder { + + private static final String BPN_REGEX = "(BPN)[LSA][\\w\\d]{10}[\\w\\d]{2}"; + + @NotEmpty + @ArraySchema(schema = @Schema(description = "Array of BPNS numbers.", example = "BPNS000000000DDD", + implementation = String.class, pattern = BPN_REGEX), maxItems = Integer.MAX_VALUE) + private List<@Pattern(regexp = BPN_REGEX) String> incidentBPNSs; + + @NotEmpty + @Valid + @ArraySchema(schema = @Schema(description = "Keys array contains required attributes for identify part chain entry node ", implementation = PartChainIdentificationKey.class), maxItems = Integer.MAX_VALUE) + private Set keys; + + @Schema(description = "BoM Lifecycle of the result tree.", implementation = BomLifecycle.class) + private BomLifecycle bomLifecycle; + + @URL + @Schema(description = "Callback url to notify requestor when job processing is finished. There are four uri variable placeholders that can be used: orderId, batchId, orderState and batchState.", + example = "https://hostname.com/callback?orderId={orderId}&batchId={batchId}&orderState={orderState}&batchState={batchState}") + private String callbackUrl; + + @Schema(implementation = Integer.class, minimum = MIN_BATCH_SIZE_DESC, maximum = MAX_BATCH_SIZE_DESC, defaultValue = DEFAULT_BATCH_SIZE_DESC, + description = "Size of the batch.") + @Min(MIN_BATCH_SIZE) + @Max(MAX_BATCH_SIZE) + @Mod10(message = "Batch size value must be mod 10 compliant") + private Integer batchSize = DEFAULT_BATCH_SIZE; + + @Schema(implementation = Integer.class, minimum = MIN_TIMEOUT_DESC, maximum = MAX_TIMEOUT_DESC, defaultValue = DEFAULT_TIMEOUT_DESC, + description = "Timeout in seconds for the complete batch order processing.") + @Min(MIN_TIMEOUT) + @Max(MAX_TIMEOUT) + private Integer timeout = DEFAULT_TIMEOUT; + + @Schema(implementation = Integer.class, minimum = MIN_JOB_TIMEOUT_DESC, maximum = MAX_JOB_TIMEOUT_DESC, defaultValue = DEFAULT_JOB_TIMEOUT_DESC, + description = "Timeout in seconds for each job processing inside the complete order.") + @Min(MIN_JOB_TIMEOUT) + @Max(MAX_JOB_TIMEOUT) + private Integer jobTimeout = DEFAULT_JOB_TIMEOUT; + + @Schema(implementation = BatchStrategy.class, description = "The strategy how the batch is processed internally in IRS.") + private BatchStrategy batchStrategy; + + + /** + * Validation constants + */ + @NoArgsConstructor(access = AccessLevel.PRIVATE) + /* package */ static final class RegisterBatchOrderConstants { + /* package */ static final String MIN_BATCH_SIZE_DESC = "10"; + /* package */ static final String MAX_BATCH_SIZE_DESC = "100"; + /* package */ static final String DEFAULT_BATCH_SIZE_DESC = "20"; + /* package */ static final int MIN_BATCH_SIZE = 10; + /* package */ static final int MAX_BATCH_SIZE = 100; + /* package */ static final int DEFAULT_BATCH_SIZE = 20; + /* package */ static final String MIN_TIMEOUT_DESC = "60"; + /* package */ static final String MAX_TIMEOUT_DESC = "86400"; + /* package */ static final String DEFAULT_TIMEOUT_DESC = "43200"; + /* package */ static final int MIN_TIMEOUT = 60; + /* package */ static final int MAX_TIMEOUT = 86_400; + /* package */ static final int DEFAULT_TIMEOUT = 43_200; + /* package */ static final String MIN_JOB_TIMEOUT_DESC = "60"; + /* package */ static final String MAX_JOB_TIMEOUT_DESC = "7200"; + /* package */ static final String DEFAULT_JOB_TIMEOUT_DESC = "3600"; + /* package */ static final int MIN_JOB_TIMEOUT = 60; + /* package */ static final int MAX_JOB_TIMEOUT = 7200; + /* package */ static final int DEFAULT_JOB_TIMEOUT = 3600; + /* package */ static final String GLOBAL_ASSET_ID_REGEX = "^urn:uuid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"; + } +} diff --git a/irs-policy-store/pom.xml b/irs-policy-store/pom.xml index 7b124de253..cafb6ea7bf 100644 --- a/irs-policy-store/pom.xml +++ b/irs-policy-store/pom.xml @@ -37,17 +37,10 @@ io.minio minio - ${minio.version} - - - com.fasterxml.jackson.core - jackson-databind - - - org.bouncycastle - bcprov-jdk15on - - + + + org.xerial.snappy + snappy-java org.springframework.boot diff --git a/pom.xml b/pom.xml index b973a61f46..e9e209dce3 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ 1.3.0-SNAPSHOT - 3.1.3 + 3.1.4 2.2.0 1.11.4 1.9.0 @@ -89,6 +89,7 @@ 3.3.0 3.1.0 0.0.1-SNAPSHOT + 1.1.10.5 @@ -98,6 +99,27 @@ micrometer-registry-prometheus ${micrometer.version} + + io.minio + minio + ${minio.version} + + + org.bouncycastle + bcprov-jdk15on + + + snappy-java + org.xerial.snappy + + + + + + org.xerial.snappy + snappy-java + ${snappy-java.version} +