/* * Copyright 2025 Google. This software is provided as-is, without warranty or representation for any use or purpose. * Your use of it is subject to your agreement with Google. */ package com.example.service.conversation; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.google.cloud.dlp.v2.DlpServiceClient; import com.google.privacy.dlp.v2.ByteContentItem; import com.google.privacy.dlp.v2.ContentItem; import com.google.privacy.dlp.v2.InspectConfig; import com.google.privacy.dlp.v2.InspectContentRequest; import com.google.privacy.dlp.v2.InspectContentResponse; import com.google.privacy.dlp.v2.Likelihood; import com.google.privacy.dlp.v2.LocationName; import com.google.protobuf.ByteString; import com.example.util.TextObfuscator; import reactor.core.publisher.Mono; /** Implements a data loss prevention service by integrating with the Google Cloud Data Loss Prevention (DLP) API. This service is responsible for scanning a given text input to identify and obfuscate sensitive information based on a specified DLP template. If the DLP API detects sensitive findings, the original text is obfuscated to protect user data; otherwise, the original text is returned. */ @Service public class DataLossPreventionImpl implements DataLossPrevention { private static final Logger logger = LoggerFactory.getLogger(DataLossPreventionImpl.class); private final String projectId; private final String location; private final DlpServiceClient dlpServiceClient; public DataLossPreventionImpl( DlpServiceClient dlpServiceClient, @Value("${google.cloud.project}") String projectId, @Value("${google.cloud.location}") String location) { this.dlpServiceClient = dlpServiceClient; this.projectId = projectId; this.location = location; } @Override public Mono getObfuscatedString(String text, String templateId) { ByteContentItem byteContentItem = ByteContentItem.newBuilder() .setType(ByteContentItem.BytesType.TEXT_UTF8) .setData(ByteString.copyFromUtf8(text)) .build(); ContentItem contentItem = ContentItem.newBuilder().setByteItem(byteContentItem).build(); Likelihood minLikelihood = Likelihood.VERY_UNLIKELY; InspectConfig.FindingLimits findingLimits = InspectConfig.FindingLimits.newBuilder().setMaxFindingsPerItem(0) .build(); InspectConfig inspectConfig = InspectConfig.newBuilder() .setMinLikelihood(minLikelihood) .setLimits(findingLimits) .setIncludeQuote(true) .build(); String inspectTemplateName = String.format("projects/%s/locations/%s/inspectTemplates/%s", projectId, location, templateId); InspectContentRequest request = InspectContentRequest.newBuilder() .setParent(LocationName.of(projectId, location).toString()) .setInspectTemplateName(inspectTemplateName) .setInspectConfig(inspectConfig) .setItem(contentItem) .build(); ApiFuture futureResponse = dlpServiceClient.inspectContentCallable() .futureCall(request); return Mono.create( sink -> ApiFutures.addCallback( futureResponse, new ApiFutureCallback<>() { @Override public void onFailure(Throwable t) { sink.error(t); } @Override public void onSuccess(InspectContentResponse result) { sink.success(result); } }, Runnable::run)) .map(response -> { logger.info("DLP {} Findings: {}", templateId, response.getResult().getFindingsCount()); return response.getResult().getFindingsCount() > 0 ? TextObfuscator.obfuscate(response, text) : text; }).onErrorResume(e -> { e.printStackTrace(); return Mono.just(text); }); } }