Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions bruno-api/DossierFacile/api-tenant/folder.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
meta {
name: api-tenant
seq: 1
}

auth {
mode: inherit
}
153 changes: 153 additions & 0 deletions bruno-api/DossierFacile/api-tenant/webhook/document-ia.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
meta {
name: document-ia
type: http
seq: 1
}

post {
url: {{server}}/api/webhook/v1/document-ia
body: json
auth: apikey
}

auth:apikey {
key: X-Api-Key
value: Azerty123!
placement: header
}

body:json {
{
"id": "33cae79f-09b0-4717-a9d5-e1d106d8c5ad",
"status": "SUCCESS",
"data": {
"total_processing_time_ms": 13861,
"result": {
"classification": {
"document_type": "cni",
"confidence": 0.99,
"explanation": "Présence des mentions 'République Française' et 'Carte Nationale d'Identité'. Contient nom, prénoms, date et lieu de naissance, numéro de document à 12 chiffres, et dates de validité."
},
"extraction": {
"type": "cni",
"properties": [
{
"name": "numero_document",
"value": "MCLND07Y6",
"type": "string"
},
{
"name": "date_expiration",
"value": "10/08/2031",
"type": "string"
},
{
"name": "nom",
"value": "ISumame",
"type": "string"
},
{
"name": "prenom",
"value": "Nicolas",
"type": "string"
},
{
"name": "date_naissance",
"value": "07/10/1993",
"type": "string"
},
{
"name": "lieu_naissance",
"value": "ÉCULLY",
"type": "string"
},
{
"name": "nationalite",
"value": "FRA",
"type": "string"
},
{
"name": "bande_mrz",
"value": "MCLND07Y6\nNICOLAS PATRICK\nFRA9310073M380820<<<<<<<<<<<<<<<<<<\n07Y600000000000000000000000000",
"type": "string"
}
]
},
"barcodes": [
{
"position": {
"top_left": [
245,
176
],
"top_right": [
446,
180
],
"bottom_right": [
443,
385
],
"bottom_left": [
242,
383
]
},
"page_number": 2,
"type": "DATA_MATRIX",
"is_valid": true,
"data": {
"fields": {
"60": "NICOLAS/PATRICK",
"62": "SAGON",
"65": "ID",
"66": "MCLND07Y6",
"67": "FR",
"68": "M",
"6C": "FR"
},
"country": "FR",
"doc_type": "07",
"perimeter": "01"
}
}
],
"workflow_metadata": [
{
"step_name": "DownloadFileStep",
"execution_time": 0.18137345800641924
},
{
"step_name": "PreprocessFileStep",
"execution_time": 0.15958420798415318
},
{
"step_name": "ExtractBarcodeData",
"execution_time": 0.0959704170236364
},
{
"step_name": "ExtractContentMarkerOcrStep",
"execution_time": 3.5028272090130486
},
{
"step_name": "LLMClassifyDocumentStep",
"execution_time": 2.7598340830008965,
"request_tokens": 1117,
"response_tokens": 79
},
{
"step_name": "LLMExtractDocumentStep",
"execution_time": 6.878677457978483,
"request_tokens": 1581,
"response_tokens": 170
}
]
}
}
}
}

settings {
encodeUrl: true
timeout: 0
}
8 changes: 8 additions & 0 deletions bruno-api/DossierFacile/api-tenant/webhook/folder.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
meta {
name: webhook
seq: 1
}

auth {
mode: inherit
}
9 changes: 9 additions & 0 deletions bruno-api/DossierFacile/bruno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"version": "1",
"name": "DossierFacile",
"type": "collection",
"ignore": [
"node_modules",
".git"
]
}
3 changes: 3 additions & 0 deletions bruno-api/DossierFacile/environments/Local.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
vars {
server: http://localhost:8090
}
4 changes: 4 additions & 0 deletions dossierfacile-api-tenant/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ brevo.template.id.tenant.validated.dossier.not.valid.w.partner=
link.after.denied.default=
link.after.validated.default=
link.shared.property=

# Document IA
document.ia.api.base.url=
document.ia.api.key=
```

## LogStash :
Expand Down
6 changes: 6 additions & 0 deletions dossierfacile-api-tenant/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-imaging</artifactId>
<version>1.0-alpha3</version>
<scope>compile</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fr.dossierfacile.api.front.config;

import fr.dossierfacile.api.front.config.filter.ConnectionContextFilter;
import fr.dossierfacile.api.front.config.filter.DossierFacileWebhookApiKeyFilter;
import fr.dossierfacile.api.front.security.PartnerAuthorizationManager;
import fr.dossierfacile.logging.request.Oauth2LoggingHandler;
import lombok.RequiredArgsConstructor;
Expand All @@ -17,6 +18,7 @@
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.security.web.header.writers.StaticHeadersWriter;
Expand All @@ -36,13 +38,16 @@ public class ResourceServerConfig {

private final AuthenticationEntryPoint authenticationEntryPoint = new Oauth2LoggingHandler();

private final DossierFacileWebhookApiKeyFilter dossierFacileWebhookApiKeyFilter;

@Value("${resource.server.config.csp}")
private String configCsp;

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.addFilterBefore(new ConnectionContextFilter(), FilterSecurityInterceptor.class)
.addFilterBefore(dossierFacileWebhookApiKeyFilter, AuthorizationFilter.class)
.headers(headers -> headers
.addHeaderWriter(new StaticHeadersWriter("X-Content-Type-Options", "nosniff"))
.contentTypeOptions(withDefaults())
Expand All @@ -67,7 +72,8 @@ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
"/api/stats/**",
"/api/onetimesecret/**",
"/error",
"/actuator/health").permitAll()
"/actuator/health",
"/api/webhook/**").permitAll()
.requestMatchers("/api-partner/**").access(apiPartnerAuthorizationManager())
.requestMatchers("/dfc/api/**").access(dfcPartnerServiceAuthorizationManager())
.requestMatchers("/dfc/**").hasAuthority("SCOPE_dfc")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package fr.dossierfacile.api.front.config.filter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
public class DossierFacileWebhookApiKeyFilter extends OncePerRequestFilter {

private final AntPathMatcher pathMatcher = new AntPathMatcher();
private final String expectedApiKey;

public DossierFacileWebhookApiKeyFilter(@Value("${dossier.facile.webhook.api.key}") String expectedApiKey) {
this.expectedApiKey = expectedApiKey;
}

@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
// Ne filtre que les webhooks Document IA
return !pathMatcher.match("/api/webhook/**", request.getRequestURI());
}

@Override
protected void doFilterInternal(
HttpServletRequest request,
@NotNull HttpServletResponse response,
@NotNull FilterChain filterChain
) throws ServletException, IOException {

String apiKey = request.getHeader("X-Api-Key");

if (expectedApiKey == null || expectedApiKey.isBlank() || !expectedApiKey.equals(apiKey)) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write("{\"error\":\"unauthorized\"}");
return;
}

filterChain.doFilter(request, response);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package fr.dossierfacile.api.front.controller;

import fr.dossierfacile.api.front.model.documentIA.WebhookModel;
import fr.dossierfacile.api.front.service.interfaces.DocumentIAService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/api/webhook")
public class DocumentIAController {

private final DocumentIAService documentIAService;

@PostMapping("/v1/document-ia")
public ResponseEntity<Void> receiveDocumentIAWebhook(
HttpServletRequest request,
@Valid @RequestBody WebhookModel body
) {

documentIAService.handleWebhookCallback(body);

return ResponseEntity.accepted().build();
}
}
Loading