Bug: fix(diesel_models, api_models): mask `poc_email` and `data_value` for DashboardMetadata



## Current behaviour

- `poc_email` isn't masked for `DashboardMetadata` which leads to it being logged both at API and diesel query level.
Example:

<img width="1114" alt="Image" src="https://github.com/user-attachments/assets/8aa98114-b492-40bf-bddf-68323f86ffc0" />

## Proposed Changes

- (api_models): change the `poc_email` type to `Secret<String>` 
  - Add some `pii::Email` validation to check for input validity
- (diesel_models): change `data_value` to `Secret<serde_json::Value>`
diff --git a/crates/api_models/src/user/dashboard_metadata.rs b/crates/api_models/src/user/dashboard_metadata.rs
index 1d606ef63e8..f15029a7a49 100644
--- a/crates/api_models/src/user/dashboard_metadata.rs
+++ b/crates/api_models/src/user/dashboard_metadata.rs
@@ -94,7 +94,7 @@ pub struct ProdIntent {
     pub business_label: Option<String>,
     pub business_location: Option<CountryAlpha2>,
     pub display_name: Option<String>,
-    pub poc_email: Option<String>,
+    pub poc_email: Option<Secret<String>>,
     pub business_type: Option<String>,
     pub business_identifier: Option<String>,
     pub business_website: Option<String>,
diff --git a/crates/diesel_models/src/user/dashboard_metadata.rs b/crates/diesel_models/src/user/dashboard_metadata.rs
index 291b4d2a3e6..004fc86693b 100644
--- a/crates/diesel_models/src/user/dashboard_metadata.rs
+++ b/crates/diesel_models/src/user/dashboard_metadata.rs
@@ -1,5 +1,6 @@
 use common_utils::id_type;
 use diesel::{query_builder::AsChangeset, Identifiable, Insertable, Queryable, Selectable};
+use masking::Secret;
 use time::PrimitiveDateTime;
 
 use crate::{enums, schema::dashboard_metadata};
@@ -12,7 +13,7 @@ pub struct DashboardMetadata {
     pub merchant_id: id_type::MerchantId,
     pub org_id: id_type::OrganizationId,
     pub data_key: enums::DashboardMetadata,
-    pub data_value: serde_json::Value,
+    pub data_value: Secret<serde_json::Value>,
     pub created_by: String,
     pub created_at: PrimitiveDateTime,
     pub last_modified_by: String,
@@ -28,7 +29,7 @@ pub struct DashboardMetadataNew {
     pub merchant_id: id_type::MerchantId,
     pub org_id: id_type::OrganizationId,
     pub data_key: enums::DashboardMetadata,
-    pub data_value: serde_json::Value,
+    pub data_value: Secret<serde_json::Value>,
     pub created_by: String,
     pub created_at: PrimitiveDateTime,
     pub last_modified_by: String,
@@ -41,7 +42,7 @@ pub struct DashboardMetadataNew {
 #[diesel(table_name = dashboard_metadata)]
 pub struct DashboardMetadataUpdateInternal {
     pub data_key: enums::DashboardMetadata,
-    pub data_value: serde_json::Value,
+    pub data_value: Secret<serde_json::Value>,
     pub last_modified_by: String,
     pub last_modified_at: PrimitiveDateTime,
 }
@@ -50,7 +51,7 @@ pub struct DashboardMetadataUpdateInternal {
 pub enum DashboardMetadataUpdate {
     UpdateData {
         data_key: enums::DashboardMetadata,
-        data_value: serde_json::Value,
+        data_value: Secret<serde_json::Value>,
         last_modified_by: String,
     },
 }
diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs
index 689762c1f43..2ea2ba1d3d8 100644
--- a/crates/router/src/core/user/dashboard_metadata.rs
+++ b/crates/router/src/core/user/dashboard_metadata.rs
@@ -1,12 +1,16 @@
+use std::str::FromStr;
+
 use api_models::user::dashboard_metadata::{self as api, GetMultipleMetaDataPayload};
 #[cfg(feature = "email")]
 use common_enums::EntityType;
+use common_utils::pii;
 use diesel_models::{
     enums::DashboardMetadata as DBEnum, user::dashboard_metadata::DashboardMetadata,
 };
 use error_stack::{report, ResultExt};
 #[cfg(feature = "email")]
 use masking::ExposeInterface;
+use masking::PeekInterface;
 #[cfg(feature = "email")]
 use router_env::logger;
 
@@ -447,6 +451,11 @@ async fn insert_metadata(
             metadata
         }
         types::MetaData::ProdIntent(data) => {
+            if let Some(poc_email) = &data.poc_email {
+                let inner_poc_email = poc_email.peek().as_str();
+                pii::Email::from_str(inner_poc_email)
+                    .change_context(UserErrors::EmailParsingError)?;
+            }
             let mut metadata = utils::insert_user_scoped_metadata_to_db(
                 state,
                 user.user_id.clone(),
diff --git a/crates/router/src/services/email/types.rs b/crates/router/src/services/email/types.rs
index 476cd1292f6..1b88f648678 100644
--- a/crates/router/src/services/email/types.rs
+++ b/crates/router/src/services/email/types.rs
@@ -454,7 +454,7 @@ impl BizEmailProd {
             settings: state.conf.clone(),
             subject: consts::user::EMAIL_SUBJECT_NEW_PROD_INTENT,
             user_name: data.poc_name.unwrap_or_default().into(),
-            poc_email: data.poc_email.unwrap_or_default().into(),
+            poc_email: data.poc_email.unwrap_or_default(),
             legal_business_name: data.legal_business_name.unwrap_or_default(),
             business_location: data
                 .business_location
diff --git a/crates/router/src/utils/user/dashboard_metadata.rs b/crates/router/src/utils/user/dashboard_metadata.rs
index b5e16290fca..f5836f25fa7 100644
--- a/crates/router/src/utils/user/dashboard_metadata.rs
+++ b/crates/router/src/utils/user/dashboard_metadata.rs
@@ -10,7 +10,7 @@ use diesel_models::{
     user::dashboard_metadata::{DashboardMetadata, DashboardMetadataNew, DashboardMetadataUpdate},
 };
 use error_stack::{report, ResultExt};
-use masking::Secret;
+use masking::{ExposeInterface, PeekInterface, Secret};
 use router_env::logger;
 
 use crate::{
@@ -37,7 +37,7 @@ pub async fn insert_merchant_scoped_metadata_to_db(
             merchant_id,
             org_id,
             data_key: metadata_key,
-            data_value,
+            data_value: Secret::from(data_value),
             created_by: user_id.clone(),
             created_at: now,
             last_modified_by: user_id,
@@ -70,7 +70,7 @@ pub async fn insert_user_scoped_metadata_to_db(
             merchant_id,
             org_id,
             data_key: metadata_key,
-            data_value,
+            data_value: Secret::from(data_value),
             created_by: user_id.clone(),
             created_at: now,
             last_modified_by: user_id,
@@ -143,7 +143,7 @@ pub async fn update_merchant_scoped_metadata(
             metadata_key,
             DashboardMetadataUpdate::UpdateData {
                 data_key: metadata_key,
-                data_value,
+                data_value: Secret::from(data_value),
                 last_modified_by: user_id,
             },
         )
@@ -171,7 +171,7 @@ pub async fn update_user_scoped_metadata(
             metadata_key,
             DashboardMetadataUpdate::UpdateData {
                 data_key: metadata_key,
-                data_value,
+                data_value: Secret::from(data_value),
                 last_modified_by: user_id,
             },
         )
@@ -183,7 +183,7 @@ pub fn deserialize_to_response<T>(data: Option<&DashboardMetadata>) -> UserResul
 where
     T: serde::de::DeserializeOwned,
 {
-    data.map(|metadata| serde_json::from_value(metadata.data_value.clone()))
+    data.map(|metadata| serde_json::from_value(metadata.data_value.clone().expose()))
         .transpose()
         .change_context(UserErrors::InternalServerError)
         .attach_printable("Error Serializing Metadata from DB")
@@ -278,17 +278,18 @@ pub fn parse_string_to_enums(query: String) -> UserResult<GetMultipleMetaDataPay
     })
 }
 
-fn not_contains_string(value: &Option<String>, value_to_be_checked: &str) -> bool {
-    value
-        .as_ref()
-        .is_some_and(|mail| !mail.contains(value_to_be_checked))
+fn not_contains_string(value: Option<&str>, value_to_be_checked: &str) -> bool {
+    value.is_some_and(|mail| !mail.contains(value_to_be_checked))
 }
 
 pub fn is_prod_email_required(data: &ProdIntent, user_email: String) -> bool {
-    let poc_email_check = not_contains_string(&data.poc_email, "juspay");
-    let business_website_check = not_contains_string(&data.business_website, "juspay")
-        && not_contains_string(&data.business_website, "hyperswitch");
-    let user_email_check = not_contains_string(&Some(user_email), "juspay");
+    let poc_email_check = not_contains_string(
+        data.poc_email.as_ref().map(|email| email.peek().as_str()),
+        "juspay",
+    );
+    let business_website_check = not_contains_string(data.business_website.as_deref(), "juspay")
+        && not_contains_string(data.business_website.as_deref(), "hyperswitch");
+    let user_email_check = not_contains_string(Some(&user_email), "juspay");
 
     if (poc_email_check && business_website_check && user_email_check).not() {
         logger::info!(prod_intent_email = poc_email_check);

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [x] Bugfix
- [ ] New feature
- [x] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
- (api_models): change the `poc_email` type to `Secret<String>` 
  - Add some `pii::Email` validation to check for input validity
- (diesel_models): change `data_value` to `Secret<serde_json::Value>`

## Fixes #7128 
### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->
`poc_email` isn't masked for `DashboardMetadata` which leads to it being logged both at API and diesel query level.

Example:
<img width="1114" alt="Image" src="https://github.com/user-attachments/assets/8aa98114-b492-40bf-bddf-68323f86ffc0" />

## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
Curl to set metadata:
```bash
curl --location 'http://localhost:8080/user/data' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{TOKEN}}' \
--data '
{
    "ProdIntent": {
        "is_completed": true,
        "legal_business_name": "test",
        "business_label": "test",
        "business_location": "US",
        "display_name": "test",
        "poc_email": "test@gmail.com",
        "business_type": "test",
        "business_identifier": "test",
        "business_website": "test",
        "poc_name": "test",
        "poc_contact": "test",
        "comments": "test"
    }
} 
'
```
Result: 

<img width="815" alt="Screenshot 2025-01-28 at 3 03 49 PM" src="https://github.com/user-attachments/assets/039d9ee6-88f3-4213-92d6-0f6dba73c010" />




## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
