gtuk / diga-api-service Goto Github PK
View Code? Open in Web Editor NEWThis project is a basic api around the excellent diga-api-client It supports validation and billing.
License: Apache License 2.0
This project is a basic api around the excellent diga-api-client It supports validation and billing.
License: Apache License 2.0
Sending test codes using this service makes sense to me to try out the different api endpoints and also to test integrations of this service. However, in production setups it might be better to disallow sending requests with test codes. Potential solutions:
is_test_code
in the api responses so it can be handled at caller side.If a code validation request fails the service returns a generic response with http status 400, e.g.:
{
"timestamp": "2021-08-11T14:39:16.682+00:00",
"status": 400,
"error": "Bad Request",
"path": "/validate/AAAAAAAAAAAAAAAA"
}
In the case where the request to diga-api-client
succeeds but response.isHasError() == true it would be helpful if the original errors from the DigaCodeValidationResponse
could be included in the response from the diga-api-service
as well.
Using the numeric error codes seems more consistent as they are language independent and also found in official DiGA documentation.
A possible response format could be:
{
"timestamp": "2021-08-11T14:39:16.682+00:00",
"status": 400,
"error": "Bad Request",
"digaErrorCode": 102,
"digaErrorText": "Freischaltcode / Rezeptcode nicht gefunden: AAAA AAAA AAAA AAAA", // optional
"path": "/validate/AAAAAAAAAAAAAAAA"
}
One complication is that AbstractDigaApiResponse
defines errors
as a List so it might be that the response format should reflect this.
{
"timestamp":"2021-08-11T14:39:16.682+00:00",
"status":400,
"error":"Bad Request",
"digaErrors":[
{
"errorCode":102,
"errorText":"Freischaltcode / Rezeptcode nicht gefunden: AAAA AAAA AAAA AAAA" // optional
}
],
"path":"/validate/AAAAAAAAAAAAAAAA"
}
Currently the response for a diga test code when DISABLE_TESTCODES=true
is:
{
"timestamp": "2021-08-23T07:15:50.545+00:00",
"status": 400,
"error": "Bad Request",
"message": "Testcodes are not allowed",
"path": "/validate/77AAAAAAAAAAAGIS"
}
It could make sense to use return 403 Forbidden
to denote this. It fits the semantics of the status code:
The HTTP 403 Forbidden client error status response code indicates that the server understood the request but refuses to authorize it.
And it is clear(er) that the error is different from the normal code validation error.
The proposed response would instead be:
{
"timestamp": "2021-08-23T10:14:44.185+00:00",
"status": 403,
"error": "Forbidden",
"message": "Testcodes are not allowed",
"path": "/validate/77AAAAAAAAAAAGIS"
}
Hi!
First of all thanks for this project, it helped us a lot.
We would like to use the service to validate the codes for two DiGAs in the future. As far as I see this might not be possible with the current implementation. I see two possible ways at this point:
validate_diga2
and bill_diga2
) for the second DiGA and implement some way to switch between the configurations.Am I going in the right direction or am I missing something?
Thanks!
We have noticed in section 4.4 - Protokollierung
here that it is required to log the interactions with the DiGA api.
Die Protokollierung muss die folgenden Mindestinhalte umfassen:
- Eindeutige Bezeichnung der Kommunikationspartner (Institutionskennzeichen)
- Zeitstempel im Format „yyyy-MM-dd HH:mm:ss“, basierend auf deutscher Zeit
- Prozesskennzeichen (Prüfung Freischaltcode / Rezeptcode / Abrechnung)
- Freischaltcode / Rezeptcode
- Verarbeitungskennzeichen (fehlerfrei/fehlerhaft)
Translated fields:
- ik numbers
- timestamp in German timezone
- value for `verfahren`
- prescription code
- flag if valid or not
In the current json response of this service we only get parts of this data. Also it might be a good idea to store the response xml (on caller-side) after making the validation request. Therefore, it would be great if this service could return the xml file in addition to the existing response format. In its' simplest form the xml could just be returned as text string.
Current Response
{
"code": "77AAAAAAAAAAAAAX",
"digavId": "00451000",
"dayOfServiceProvision": "2021-06-02"
}
Proposed Change
{
"code": "77AAAAAAAAAAAAAX",
"digavId": "00451000",
"dayOfServiceProvision": "2021-06-02",
"xmlResponse": "<?xml version="1.0" ...>..."
}
All the required data for logging can be extracted from the xml_response
on the caller-side. Additionally, it could make sense to return a boolean field isTestCode
which denotes if the code was a test code or not.
What do you think @gtuk ?
Using Java 17 and the new 2.0.0 release I seem to run into spring-projects/spring-boot#28837:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.5.0)2023-11-14 15:46:05.429 INFO 76200 --- [ main] dev.gtuk.diga.ApplicationKt : Starting ApplicationKt vv2.0.0 using Java 17.0.9 on echo with PID 76200 (/.../digaapibuildout/diga-api-service-v2.0.0.jar started by icemac in /.../digaapibuildout)
2023-11-14 15:46:05.430 INFO 76200 --- [ main] dev.gtuk.diga.ApplicationKt : No active profile set, falling back to default profiles: default
2023-11-14 15:46:06.045 INFO 76200 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 6000 (http)
2023-11-14 15:46:06.050 INFO 76200 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-11-14 15:46:06.050 INFO 76200 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.46]
2023-11-14 15:46:06.079 INFO 76200 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-11-14 15:46:06.079 INFO 76200 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 618 ms
2023-11-14 15:46:07.082 INFO 76200 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 6000 (http) with context path ''
2023-11-14 15:46:07.088 INFO 76200 --- [ main] dev.gtuk.diga.ApplicationKt : Started ApplicationKt in 1.87 seconds (JVM running for 2.091)
2023-11-14 15:46:07.088 INFO 76200 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state LivenessState changed to CORRECT
2023-11-14 15:46:07.089 INFO 76200 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
2023-11-14 15:46:10.161 INFO 76200 --- [nio-6000-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-11-14 15:46:10.161 INFO 76200 --- [nio-6000-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-11-14 15:46:10.162 INFO 76200 --- [nio-6000-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2023-11-14 15:46:10.596 ERROR 76200 --- [nio-6000-exec-1] com.alextherapeutics.diga.DigaApiClient : Failed to validate DiGA code 77AAAAAAAAAAAAAXcom.alextherapeutics.diga.DigaEncryptionException: Diga encryption failed due to exception
at com.alextherapeutics.diga.model.DigaEncryption.encrypt(DigaEncryption.java:61) ~[diga-api-client-2.0.0.jar!/:na]
at com.alextherapeutics.diga.DigaApiClient.performCodeValidation(DigaApiClient.java:213) ~[diga-api-client-2.0.0.jar!/:na]
at com.alextherapeutics.diga.DigaApiClient.sendTestCodeValidationRequest(DigaApiClient.java:152) ~[diga-api-client-2.0.0.jar!/:na]
at dev.gtuk.diga.DigaService.verify(DigaService.kt:75) ~[classes!/:v2.0.0]
at dev.gtuk.diga.AppController.verify(AppController.kt:18) ~[classes!/:v2.0.0]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197) ~[spring-web-5.3.7.jar!/:5.3.7]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141) ~[spring-web-5.3.7.jar!/:5.3.7]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.7.jar!/:5.3.7]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894) ~[spring-webmvc-5.3.7.jar!/:5.3.7]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.7.jar!/:5.3.7]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.7.jar!/:5.3.7]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1063) ~[spring-webmvc-5.3.7.jar!/:5.3.7]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.7.jar!/:5.3.7]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.7.jar!/:5.3.7]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.7.jar!/:5.3.7]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:626) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.7.jar!/:5.3.7]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.46.jar!/:na]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.7.jar!/:5.3.7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.7.jar!/:5.3.7]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.7.jar!/:5.3.7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.7.jar!/:5.3.7]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.7.jar!/:5.3.7]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.7.jar!/:5.3.7]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707) ~[tomcat-embed-core-9.0.46.jar!/:na]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.46.jar!/:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.46.jar!/:na]
at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]
Caused by: de.tk.opensource.secon.SeconException: org.bouncycastle.cms.CMSException: cannot create key generator: JCE cannot authenticate the provider BC
at de.tk.opensource.secon.SECON.lambda$callable$4(SECON.java:268) ~[secon-tool-1.2.0.jar!/:na]
at global.namespace.fun.io.api.Socket.accept(Socket.java:109) ~[fun-io-api-2.4.0.jar!/:2.4.0]
at global.namespace.fun.io.spi.Copy.lambda$copy$3(Copy.java:91) ~[fun-io-spi-2.4.0.jar!/:2.4.0]
at global.namespace.fun.io.api.Socket.accept(Socket.java:110) ~[fun-io-api-2.4.0.jar!/:2.4.0]
at global.namespace.fun.io.spi.Copy.copy(Copy.java:91) ~[fun-io-spi-2.4.0.jar!/:2.4.0]
at global.namespace.fun.io.bios.BIOS.copy(BIOS.java:537) ~[fun-io-bios-2.4.0.jar!/:2.4.0]
at de.tk.opensource.secon.SECON.lambda$copy$3(SECON.java:246) ~[secon-tool-1.2.0.jar!/:na]
at de.tk.opensource.secon.SECON.lambda$callable$4(SECON.java:262) ~[secon-tool-1.2.0.jar!/:na]
at de.tk.opensource.secon.SECON.call(SECON.java:256) ~[secon-tool-1.2.0.jar!/:na]
at de.tk.opensource.secon.SECON.copy(SECON.java:245) ~[secon-tool-1.2.0.jar!/:na]
at com.alextherapeutics.diga.model.DigaEncryption.encrypt(DigaEncryption.java:54) ~[diga-api-client-2.0.0.jar!/:na]
... 54 common frames omitted
Caused by: org.bouncycastle.cms.CMSException: cannot create key generator: JCE cannot authenticate the provider BC
at org.bouncycastle.cms.jcajce.EnvelopedDataHelper.createKeyGenerator(Unknown Source) ~[bcpkix-jdk15on-1.70.jar!/:1.70.00.0]
at org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder$CMSOutputEncryptor.(Unknown Source) ~[bcpkix-jdk15on-1.70.jar!/:1.70.00.0]
at org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder.build(Unknown Source) ~[bcpkix-jdk15on-1.70.jar!/:1.70.00.0]
at de.tk.opensource.secon.DefaultSubscriber.encrypt(DefaultSubscriber.java:215) ~[secon-tool-1.2.0.jar!/:na]
at de.tk.opensource.secon.DefaultSubscriber.lambda$encrypt$2(DefaultSubscriber.java:220) ~[secon-tool-1.2.0.jar!/:na]
at de.tk.opensource.secon.Streams.lambda$fixOutputstreamClose$1(Streams.java:48) ~[secon-tool-1.2.0.jar!/:na]
at global.namespace.fun.io.api.function.XFunction.lambda$compose$0(XFunction.java:32) ~[fun-io-api-2.4.0.jar!/:2.4.0]
at global.namespace.fun.io.api.Socket.lambda$map$0(Socket.java:138) ~[fun-io-api-2.4.0.jar!/:2.4.0]
at de.tk.opensource.secon.SECON.lambda$callable$4(SECON.java:262) ~[secon-tool-1.2.0.jar!/:na]
... 64 common frames omitted
Caused by: java.security.NoSuchProviderException: JCE cannot authenticate the provider BC
at java.base/javax.crypto.JceSecurity.getInstance(JceSecurity.java:131) ~[na:na]
at java.base/javax.crypto.KeyGenerator.getInstance(KeyGenerator.java:286) ~[na:na]
at org.bouncycastle.jcajce.util.NamedJcaJceHelper.createKeyGenerator(Unknown Source) ~[bcprov-jdk15on-1.70.jar!/:1.70.0]
... 73 common frames omitted
Caused by: java.lang.IllegalStateException: zip file closed
at java.base/java.util.zip.ZipFile.ensureOpen(ZipFile.java:840) ~[na:na]
at java.base/java.util.zip.ZipFile.getManifestName(ZipFile.java:1066) ~[na:na]
at java.base/java.util.zip.ZipFile$1.getManifestName(ZipFile.java:1125) ~[na:na]
at java.base/javax.crypto.JarVerifier.verifySingleJar(JarVerifier.java:464) ~[na:na]
at java.base/javax.crypto.JarVerifier.verifyJars(JarVerifier.java:320) ~[na:na]
at java.base/javax.crypto.JarVerifier.verify(JarVerifier.java:263) ~[na:na]
at java.base/javax.crypto.ProviderVerifier.verify(ProviderVerifier.java:130) ~[na:na]
at java.base/javax.crypto.JceSecurity.verifyProvider(JceSecurity.java:190) ~[na:na]
at java.base/javax.crypto.JceSecurity.getVerificationResult(JceSecurity.java:218) ~[na:na]
at java.base/javax.crypto.JceSecurity.getInstance(JceSecurity.java:128) ~[na:na]
... 75 common frames omitted
According to spring-projects/spring-boot@33c5e12 this issue was fixed in 3.2.0rc1.
Do you think it is possible to try out the latest version (currently 3.2.0rc2) to get rid of this error? It currently prevents from using the new release and Java 17.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.