refactorin security trace

This commit is contained in:
Ramon Ramirez 2026-01-19 16:27:11 -04:00
parent 91c0047f99
commit 19b24e1688
12 changed files with 267 additions and 104 deletions

View File

@ -40,6 +40,14 @@ public class MessageHelper {
this.errorMappings = initializeErrorMappings(); this.errorMappings = initializeErrorMappings();
} }
public Response handleSuccess(Object data, String statusCode) {
log.info(
"Respuesta exitosa controlada: {}",
statusCode
);
return buildResponse(data, statusCode);
}
public Response handleException(HttpStatusCodeException exception) { public Response handleException(HttpStatusCodeException exception) {
log.error( log.error(
"Error interno controlado: {} -> {}", "Error interno controlado: {} -> {}",
@ -110,6 +118,25 @@ public class MessageHelper {
.build(); .build();
} }
private Response buildResponse(Object data, String statusCode) {
ErrorMapping mapping = errorMappings.getOrDefault(
statusCode, errorMappings.getOrDefault(
statusCode, createDefaultMapping()
)
);
StatusResponse status = createError(mapping, null);
log.error(
"[Success] Message {} -> {}",
statusCode,
status.getMessage()
);
return Response.status(mapping.getHttpCode())
.entity(new ApiResponse<>(data, status))
.build();
}
private Map<String, ErrorMapping> initializeErrorMappings() { private Map<String, ErrorMapping> initializeErrorMappings() {
try { try {
String json; String json;

View File

@ -144,7 +144,9 @@ public class SerializationHelper {
String channelBase64 = encodeStringToBase64(channelOrigin); String channelBase64 = encodeStringToBase64(channelOrigin);
for (Object value : operationSortedMap.values()) { for (Object value : operationSortedMap.values()) {
concatenatedValues.append(value.toString()); if(!Objects.isNull(value)) {
concatenatedValues.append(value);
}
} }
String finalString = concatenatedValues + channelBase64; String finalString = concatenatedValues + channelBase64;

View File

@ -0,0 +1,17 @@
package com.banesco.common.domain.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class Device {
private String deviceType;
private String deviceDescription;
private String deviceIp;
private String deviceSessionReference;
}

View File

@ -58,103 +58,103 @@ public class HttpRequest {
private boolean logResponseBody = true; private boolean logResponseBody = true;
public static <T> HttpRequest forApiResponse( public static <T> HttpRequest forApiResponse(
String url, String url,
HttpMethod method, HttpMethod method,
Class<T> dataType Class<T> dataType
) { ) {
return HttpRequest.builder() return HttpRequest.builder()
.url(url) .url(url)
.method(method) .method(method)
.responseType(ApiResponse.class) .responseType(ApiResponse.class)
.genericType(dataType) .genericType(dataType)
.apiResponse(true) .apiResponse(true)
.build(); .build();
} }
public static <T> HttpRequest forApiResponseList( public static <T> HttpRequest forApiResponseList(
String url, String url,
HttpMethod method, HttpMethod method,
Class<T> elementType Class<T> elementType
) { ) {
return HttpRequest.builder() return HttpRequest.builder()
.url(url) .url(url)
.method(method) .method(method)
.responseType(ApiResponse.class) .responseType(ApiResponse.class)
.complexType(TypeBuilder.listOf(elementType)) .complexType(TypeBuilder.listOf(elementType))
.apiResponse(true) .apiResponse(true)
.listResponse(true) .listResponse(true)
.build(); .build();
} }
public static <T> HttpRequest forApiPrivateResponse( public static <T> HttpRequest forApiPrivateResponse(
String url, String url,
String statusSuccess, String statusSuccess,
HttpMethod method, HttpMethod method,
Class<T> successType Class<T> successType
) { ) {
return HttpRequest.builder() return HttpRequest.builder()
.url(url) .url(url)
.method(method) .method(method)
.responseType(ApiPrivateResponse.class) .responseType(ApiPrivateResponse.class)
.complexType(TypeBuilder.parametricType( .complexType(TypeBuilder.parametricType(
Either.class, Either.class,
successType, successType,
ApiPrivateError.class ApiPrivateError.class
)) ))
.apiPrivateResponse(true) .apiPrivateResponse(true)
.eitherResponse(true) .eitherResponse(true)
.errorType(ApiPrivateError.class) .errorType(ApiPrivateError.class)
.statusSuccess(statusSuccess) .statusSuccess(statusSuccess)
.build(); .build();
} }
public static <T> HttpRequest forApiPrivateResponseList( public static <T> HttpRequest forApiPrivateResponseList(
String url, String url,
String statusSuccess, String statusSuccess,
HttpMethod method, HttpMethod method,
Class<T> elementType Class<T> elementType
) { ) {
return HttpRequest.builder() return HttpRequest.builder()
.url(url) .url(url)
.method(method) .method(method)
.responseType(ApiPrivateResponse.class) .responseType(ApiPrivateResponse.class)
.complexType(TypeBuilder.parametricType( .complexType(TypeBuilder.parametricType(
Either.class, Either.class,
TypeBuilder.listOf(elementType), TypeBuilder.listOf(elementType),
ApiPrivateError.class ApiPrivateError.class
)) ))
.apiPrivateResponse(true) .apiPrivateResponse(true)
.eitherResponse(true) .eitherResponse(true)
.listResponse(true) .listResponse(true)
.errorType(ApiPrivateError.class) .errorType(ApiPrivateError.class)
.statusSuccess(statusSuccess) .statusSuccess(statusSuccess)
.build(); .build();
} }
public static <T> HttpRequest forDirectResponse( public static <T> HttpRequest forDirectResponse(
String url, String url,
HttpMethod method, HttpMethod method,
Class<T> responseType Class<T> responseType
) { ) {
return HttpRequest.builder() return HttpRequest.builder()
.url(url) .url(url)
.method(method) .method(method)
.responseType(responseType) .responseType(responseType)
.build(); .build();
} }
public static <T, U> HttpRequest forGenericResponse( public static <T, U> HttpRequest forGenericResponse(
String url, String url,
HttpMethod method, HttpMethod method,
Class<T> rawType, Class<T> rawType,
Class<U> genericType Class<U> genericType
) { ) {
return HttpRequest.builder() return HttpRequest.builder()
.url(url) .url(url)
.method(method) .method(method)
.responseType(rawType) .responseType(rawType)
.complexType(TypeBuilder.parametricType(rawType, genericType)) .complexType(TypeBuilder.parametricType(rawType, genericType))
.build(); .build();
} }
public HttpRequest withHeaders(Map<String, String> headers) { public HttpRequest withHeaders(Map<String, String> headers) {

View File

@ -6,7 +6,9 @@ public class RequestContext {
private RequestContext() {} private RequestContext() {}
private static final String REQUEST_ID = "requestId"; public static final String REQUEST_ID = "requestId";
public static final String DEVICE = "device";
public static final String DEVICE_SESSION_REFERENCE = "deviceSessionReference";
public static String getRequestId() { public static String getRequestId() {
return MDC.get(REQUEST_ID); return MDC.get(REQUEST_ID);

View File

@ -6,15 +6,88 @@ import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.container.ContainerResponseContext; import jakarta.ws.rs.container.ContainerResponseContext;
import jakarta.ws.rs.container.ContainerResponseFilter; import jakarta.ws.rs.container.ContainerResponseFilter;
import jakarta.ws.rs.ext.Provider; import jakarta.ws.rs.ext.Provider;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.UUID; import java.util.UUID;
@Slf4j
@Provider @Provider
public class RequestIdFilter implements ContainerRequestFilter, ContainerResponseFilter { public class RequestIdFilter implements ContainerRequestFilter, ContainerResponseFilter {
@Override @Override
public void filter(ContainerRequestContext requestContext) { public void filter(ContainerRequestContext requestContext) {
RequestContext.setRequestId(UUID.randomUUID().toString().substring(0, 13)); String requestId = requestContext.getHeaderString(RequestContext.DEVICE_SESSION_REFERENCE);
if (isEmpty(requestId)) {
requestId = requestContext.getUriInfo()
.getQueryParameters()
.getFirst(RequestContext.DEVICE_SESSION_REFERENCE);
}
if (isEmpty(requestId) && hasJsonBody(requestContext)) {
requestId = extractRequestIdFromBody(requestContext);
}
if (isEmpty(requestId)) {
requestId = UUID.randomUUID().toString().substring(0, 13);
}
RequestContext.setRequestId(requestId);
}
private boolean isEmpty(String value) {
return value == null || value.trim().isEmpty();
}
private boolean hasJsonBody(ContainerRequestContext context) {
try {
String method = context.getMethod();
String contentType = context.getHeaderString("Content-Type");
return ("POST".equals(method) || "PUT".equals(method))
&& contentType != null
&& contentType.contains("application/json");
} catch (Exception e) {
log.warn("La peticion no es un POST o PUT: {}", e.getMessage());
return false;
}
}
private String extractRequestIdFromBody(ContainerRequestContext context) {
try {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
context.getEntityStream().transferTo(buffer);
byte[] bodyBytes = buffer.toByteArray();
context.setEntityStream(new ByteArrayInputStream(bodyBytes));
String bodyString = new String(bodyBytes, StandardCharsets.UTF_8);
io.vertx.core.json.JsonObject jsonObject = new io.vertx.core.json.JsonObject(bodyString);
if (jsonObject.containsKey(RequestContext.DEVICE)) {
io.vertx.core.json.JsonObject device = jsonObject.getJsonObject(RequestContext.DEVICE);
if (device.containsKey(RequestContext.DEVICE_SESSION_REFERENCE)) {
return device.getString(RequestContext.DEVICE_SESSION_REFERENCE);
}
}
if (jsonObject.containsKey(RequestContext.REQUEST_ID)) {
return jsonObject.getString(RequestContext.REQUEST_ID);
}
if (jsonObject.containsKey(RequestContext.DEVICE_SESSION_REFERENCE)) {
return jsonObject.getString(RequestContext.DEVICE_SESSION_REFERENCE);
}
return null;
} catch (Exception e) {
log.error("Error extrayendo el requestId del cuerpo de la peticion: {}", e.getMessage());
return null;
}
} }
@Override @Override
@ -22,6 +95,10 @@ public class RequestIdFilter implements ContainerRequestFilter, ContainerRespons
ContainerRequestContext requestContext, ContainerRequestContext requestContext,
ContainerResponseContext responseContext ContainerResponseContext responseContext
) { ) {
RequestContext.clear(); try {
RequestContext.clear();
} catch (Exception e) {
log.error("Error limpiando el filtro: {}", e.getMessage());
}
} }
} }

View File

@ -9,6 +9,7 @@ import com.banesco.module.docservice_file_audit_trace.domain.dto.request.Docserv
import com.banesco.module.docservice_file_audit_trace.domain.dto.response.DocserviceFileAuditTraceResponse; import com.banesco.module.docservice_file_audit_trace.domain.dto.response.DocserviceFileAuditTraceResponse;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@ -28,27 +29,29 @@ public class DocserviceFileAuditTraceService implements DocserviceFileAuditTrace
} }
@Override @Override
public ApiResponse<DocserviceFileAuditTraceResponse> execute( public Response execute(
DocserviceFileAuditTraceRequest request DocserviceFileAuditTraceRequest request
) { ) {
log.info("Iniciando ejecucion para el archivo: {}", request.getDocumentName()); log.info("Iniciando ejecucion para el archivo: {}", request.getDocumentName());
Response response;
try { try {
return apiPrivate(request); ApiResponse<DocserviceFileAuditTraceResponse> apiResponse = apiPrivate(request);
} catch (ApiPrivateException e) {
log.warn( response = messageHelper.handleSuccess(
"Excepcion de la api privada: {} -> {}", apiResponse.getData(),
e.getStatusCode(), apiResponse.getStatusResponse().getStatusCode()
e.getMessage()
); );
throw HttpStatusCodeException.badRequest("400");
} catch (HttpStatusCodeException e) { } catch (HttpStatusCodeException e) {
log.error("Excepcion HTTP del api privada: {} - {}", e.getStatusCode(), e.getErrorCode()); log.error("Excepcion HTTP del api de dominio: {} - {}", e.getStatusCode(), e.getErrorCode());
throw e; response = messageHelper.handleException(e);
} catch (Exception e) { } catch (Exception e) {
log.error("Excepcion generica del api privada: {}", e.getMessage()); log.error("Excepcion generica del api de dominio: {}", e.getMessage());
throw e; response = messageHelper.handleGenericException(e);
} }
return response;
} }
private ApiResponse<DocserviceFileAuditTraceResponse> apiPrivate( private ApiResponse<DocserviceFileAuditTraceResponse> apiPrivate(

View File

@ -1,11 +1,10 @@
package com.banesco.module.docservice_file_audit_trace.application.usecase; package com.banesco.module.docservice_file_audit_trace.application.usecase;
import com.banesco.common.domain.model.ApiResponse;
import com.banesco.module.docservice_file_audit_trace.domain.dto.request.DocserviceFileAuditTraceRequest; import com.banesco.module.docservice_file_audit_trace.domain.dto.request.DocserviceFileAuditTraceRequest;
import com.banesco.module.docservice_file_audit_trace.domain.dto.response.DocserviceFileAuditTraceResponse; import jakarta.ws.rs.core.Response;
public interface DocserviceFileAuditTraceUseCase { public interface DocserviceFileAuditTraceUseCase {
ApiResponse<DocserviceFileAuditTraceResponse> execute( Response execute(
DocserviceFileAuditTraceRequest request DocserviceFileAuditTraceRequest request
); );
} }

View File

@ -1,10 +1,16 @@
package com.banesco.module.docservice_file_audit_trace.domain.dto.request; package com.banesco.module.docservice_file_audit_trace.domain.dto.request;
import com.banesco.common.domain.model.Device;
import com.banesco.common.infrastructure.context.RequestContext;
import com.banesco.module.document.domain.model.Document; import com.banesco.module.document.domain.model.Document;
import com.banesco.module.instruction.domain.model.Instruction; import com.banesco.module.instruction.domain.model.Instruction;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.netty.util.internal.StringUtil;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*; import lombok.*;
import java.util.Objects;
@Getter @Getter
@ToString @ToString
@Builder @Builder
@ -20,11 +26,15 @@ public class DocserviceFileAuditTraceRequest {
private Document document; private Document document;
@NonNull @NonNull
private Instruction procedureRequest; private Instruction procedureRequest;
@NonNull
private Device device;
@JsonIgnore
public String getDocumentName() { public String getDocumentName() {
return document.getDocumentName(); return document.getDocumentName();
} }
@JsonIgnore
public String getDocumentDirectory() { public String getDocumentDirectory() {
return document return document
.getDocumentLocation() .getDocumentLocation()
@ -32,4 +42,37 @@ public class DocserviceFileAuditTraceRequest {
.getLocationReference() .getLocationReference()
.getLocationValue(); .getLocationValue();
} }
@JsonIgnore
public String getChannelCode() {
return procedureRequest
.getInstructionPurposeType()
.name();
}
@JsonIgnore
public static DocserviceFileAuditTraceRequest fromResource(
String customerReferenceFintechId,
String appId,
DocserviceFileAuditTraceRequest request
) {
return DocserviceFileAuditTraceRequest.builder()
.customerReferenceFintechId(customerReferenceFintechId)
.appId(appId)
.document(request.getDocument())
.procedureRequest(request.getProcedureRequest())
.device(
Device.builder()
.deviceType(request.getDevice().getDeviceType())
.deviceDescription(request.getDevice().getDeviceDescription())
.deviceIp(request.getDevice().getDeviceIp())
.deviceSessionReference(
(!StringUtil.isNullOrEmpty(request.getDevice().getDeviceSessionReference()))
? request.getDevice().getDeviceSessionReference()
: RequestContext.getRequestId()
)
.build()
)
.build();
}
} }

View File

@ -1,7 +1,5 @@
package com.banesco.module.docservice_file_audit_trace.infrastructure.resource; package com.banesco.module.docservice_file_audit_trace.infrastructure.resource;
import com.banesco.common.application.helper.MessageHelper;
import com.banesco.common.domain.exception.HttpStatusCodeException;
import com.banesco.common.domain.model.ApiResponse; import com.banesco.common.domain.model.ApiResponse;
import com.banesco.common.domain.model.StatusResponse; import com.banesco.common.domain.model.StatusResponse;
import com.banesco.module.docservice_file_audit_trace.application.usecase.DocserviceFileAuditTraceUseCase; import com.banesco.module.docservice_file_audit_trace.application.usecase.DocserviceFileAuditTraceUseCase;
@ -28,14 +26,11 @@ import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
public class DocserviceFileAuditTraceResource { public class DocserviceFileAuditTraceResource {
private final DocserviceFileAuditTraceUseCase useCase; private final DocserviceFileAuditTraceUseCase useCase;
private final MessageHelper messageHelper;
@Inject @Inject
public DocserviceFileAuditTraceResource( public DocserviceFileAuditTraceResource(
MessageHelper messageHelper,
DocserviceFileAuditTraceUseCase useCase DocserviceFileAuditTraceUseCase useCase
) { ) {
this.messageHelper = messageHelper;
this.useCase = useCase; this.useCase = useCase;
} }
@ -244,6 +239,12 @@ public class DocserviceFileAuditTraceResource {
}, },
"procedureRequest": { "procedureRequest": {
"instructionPurposeType": "BOLE" "instructionPurposeType": "BOLE"
},
"device": {
"deviceType": "Mobile",
"deviceDescription": "Xiaomi Note 11 PRO",
"deviceIp": "127.0.0.1",
"deviceSessionReference": "12345678901304"
} }
} }
""" """
@ -254,12 +255,6 @@ public class DocserviceFileAuditTraceResource {
) { ) {
log.info("Iniciando consulta para instruccion de archivo: {}", request.getDocumentName()); log.info("Iniciando consulta para instruccion de archivo: {}", request.getDocumentName());
try { return useCase.execute(request);
return Response.ok(useCase.execute(request)).build();
} catch (HttpStatusCodeException e) {
return messageHelper.handleException(e);
} catch (Exception e) {
return messageHelper.handleGenericException(e);
}
} }
} }

View File

@ -54,8 +54,6 @@ public class PaymentFileClient implements PaymentFileUseCase {
paymentStatusConfig.getTimeout().getResponse() paymentStatusConfig.getTimeout().getResponse()
); );
log.debug("Request configurado: {}", request);
try { try {
ApiPrivateResponse<Either<T, ApiPrivateError>> response = ApiPrivateResponse<Either<T, ApiPrivateError>> response =
httpClientUseCase.executeApiPrivateResponse(request); httpClientUseCase.executeApiPrivateResponse(request);