From eda72392dbd45d6b066fe4abd41982ecd2462ddc Mon Sep 17 00:00:00 2001 From: cyber-titan Date: Wed, 5 Nov 2025 08:56:44 +0530 Subject: [PATCH 01/12] injiweb-1671 made credSub opt for ldp_vc Signed-off-by: cyber-titan --- .../CredentialDefinitionResponseDto.java | 1 - .../CredentialIssuerDisplayResponse.java | 2 - .../impl/LdpVcCredentialFormatHandler.java | 70 +++++++++++++++++-- 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDefinitionResponseDto.java b/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDefinitionResponseDto.java index 0f2fabaf0..abb63e359 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDefinitionResponseDto.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDefinitionResponseDto.java @@ -19,7 +19,6 @@ public class CredentialDefinitionResponseDto { @Schema(description = "Type of the Credential Definition") private List<@NotEmpty String> type; - @NotEmpty @Valid @SerializedName("credentialSubject") @JsonProperty("credentialSubject") diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialIssuerDisplayResponse.java b/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialIssuerDisplayResponse.java index cff8eb93f..0a8a13b41 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialIssuerDisplayResponse.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialIssuerDisplayResponse.java @@ -9,13 +9,11 @@ @Data public class CredentialIssuerDisplayResponse { - @NotBlank @SerializedName("name") @JsonProperty("name") @Schema(description = "Name of the Credential Issuer") private String name; - @NotBlank @SerializedName("locale") @JsonProperty("locale") @Schema(description = "Locale of the Credential Issuer") diff --git a/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java b/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java index 26b5209f8..6d86a5924 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java +++ b/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java @@ -74,21 +74,22 @@ public LinkedHashMap> loadD String userLocale) { LinkedHashMap> displayProperties = new LinkedHashMap<>(); + List orderedKeys = credentialsSupportedResponse.getOrder(); + credentialsSupportedResponse.getCredentialDefinition().setCredentialSubject(null); // LDP VC format — display config is in "credential_definition.credential_subject" if (credentialsSupportedResponse.getCredentialDefinition() == null || credentialsSupportedResponse.getCredentialDefinition().getCredentialSubject() == null) { - log.warn("Missing credential definition or credential subject for LDP VC format"); - return displayProperties; + log.info("Issuer well-known has no credential definition or credential subject for LDP VC format; falling back to claim-based display properties"); + return buildFallbackDisplayProperties(credentialProperties, orderedKeys, userLocale); } Map displayConfigMap = credentialsSupportedResponse.getCredentialDefinition().getCredentialSubject(); - List orderedKeys = credentialsSupportedResponse.getOrder(); if (displayConfigMap == null) { - log.warn("No display configuration found for LDP VC format"); - return displayProperties; + log.info("No display configuration found in issuer well-known for LDP VC format; falling back to claim-based display properties"); + return buildFallbackDisplayProperties(credentialProperties, orderedKeys, userLocale); } String resolvedLocale = LocaleUtils.resolveLocaleWithFallback(displayConfigMap, userLocale); @@ -120,4 +121,63 @@ public LinkedHashMap> loadD return displayProperties; } + + private LinkedHashMap> buildFallbackDisplayProperties( + Map credentialProperties, + List orderedKeys, String userLocale) { + + LinkedHashMap> displayProperties = new LinkedHashMap<>(); + + // Determine field order (prefer issuer-provided 'order' if any) + List fieldKeys = (orderedKeys != null && !orderedKeys.isEmpty()) + ? new ArrayList<>(orderedKeys) + : new ArrayList<>(credentialProperties.keySet()); + + // Exclude non-claim metadata + fieldKeys.remove("id"); + + // Build default display entries from claims + for (String key : fieldKeys) { + Object value = credentialProperties.get(key); + if (value == null) { + continue; + } + + CredentialIssuerDisplayResponse display = null; + + // Check if value is a Map containing display information + if (value instanceof Map) { + Map valueMap = (Map) value; + Object displayObj = valueMap.get("display"); + + if (displayObj instanceof List) { + List> displayList = (List>) displayObj; + + if (!displayList.isEmpty()) { + // Try to find matching locale + Optional> matchingDisplay = displayList.stream() + .filter(d -> LocaleUtils.matchesLocale((String) d.get("locale"), userLocale)) + .findFirst(); + + Map selectedDisplay = matchingDisplay.orElse(displayList.get(0)); + + display = new CredentialIssuerDisplayResponse(); + display.setName((String) selectedDisplay.get("name")); + display.setLocale((String) selectedDisplay.get("locale")); + } + } + } + + // Fallback to default display if no nested display found + if (display == null) { + display = new CredentialIssuerDisplayResponse(); + display.setName(camelToTitleCase(key)); + display.setLocale("en"); + } + + displayProperties.put(key, Map.of(display, value)); + } + + return displayProperties; + } } From af375dde9c0d0b7a55732795a92cd2fbe687c338 Mon Sep 17 00:00:00 2001 From: cyber-titan Date: Thu, 6 Nov 2025 13:07:40 +0530 Subject: [PATCH 02/12] injiweb-1671-credSub-optional updated fix for sdjwt & testcases Signed-off-by: cyber-titan --- .../impl/LdpVcCredentialFormatHandler.java | 1 - .../impl/VcSdJwtCredentialFormatHandler.java | 85 ++++++- .../LdpVcCredentialFormatHandlerTest.java | 233 +++++++++++++++++- .../VcSdJwtCredentialFormatHandlerTest.java | 233 ++++++++++++++++++ ...lIssuerWellknownResponseValidatorTest.java | 9 +- 5 files changed, 538 insertions(+), 23 deletions(-) diff --git a/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java b/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java index 6d86a5924..c108a9047 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java +++ b/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java @@ -76,7 +76,6 @@ public LinkedHashMap> loadD LinkedHashMap> displayProperties = new LinkedHashMap<>(); List orderedKeys = credentialsSupportedResponse.getOrder(); - credentialsSupportedResponse.getCredentialDefinition().setCredentialSubject(null); // LDP VC format — display config is in "credential_definition.credential_subject" if (credentialsSupportedResponse.getCredentialDefinition() == null || credentialsSupportedResponse.getCredentialDefinition().getCredentialSubject() == null) { diff --git a/src/main/java/io/mosip/mimoto/service/impl/VcSdJwtCredentialFormatHandler.java b/src/main/java/io/mosip/mimoto/service/impl/VcSdJwtCredentialFormatHandler.java index f3a522dce..04f2473c3 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/VcSdJwtCredentialFormatHandler.java +++ b/src/main/java/io/mosip/mimoto/service/impl/VcSdJwtCredentialFormatHandler.java @@ -55,6 +55,21 @@ public LinkedHashMap> loadD LinkedHashMap> displayProperties = new LinkedHashMap<>(); + // Start with ordered fields + Set orderedKeys = Optional.ofNullable(credentialsSupportedResponse.getOrder()) + .map(LinkedHashSet::new) // preserve order + .orElse(new LinkedHashSet<>()); + + // Add remaining keys from credentialProperties that are not already in orderedKeys + for (String key : credentialProperties.keySet()) { + orderedKeys.add(key); // Set ensures no duplicates + } + + if (credentialsSupportedResponse.getClaims() == null || credentialsSupportedResponse.getClaims().isEmpty()) { + log.info("Issuer well-known has no claims for SD-JWT format; falling back to claim-based display properties"); + return buildFallbackDisplayProperties(credentialProperties, orderedKeys, userLocale); + } + // Extract raw claims and convert to DTOs Map rawClaims = Optional.ofNullable(credentialsSupportedResponse.getClaims()) .map(map -> (map.size() == 1 && map.values().iterator().next() instanceof Map) @@ -69,7 +84,8 @@ public LinkedHashMap> loadD )); if (convertedClaimsMap.isEmpty()) { - log.warn("No display configuration found for SD-JWT format"); + log.info("No display configuration found for SD-JWT format"); + return buildFallbackDisplayProperties(credentialProperties, orderedKeys, userLocale); } String resolvedLocale = LocaleUtils.resolveLocaleWithFallback(convertedClaimsMap, userLocale); @@ -84,16 +100,6 @@ public LinkedHashMap> loadD }); } - // Start with ordered fields - Set orderedKeys = Optional.ofNullable(credentialsSupportedResponse.getOrder()) - .map(LinkedHashSet::new) // preserve order - .orElse(new LinkedHashSet<>()); - - // Add remaining keys from credentialProperties that are not already in orderedKeys - for (String key : credentialProperties.keySet()) { - orderedKeys.add(key); // Set ensures no duplicates - } - for (String key : orderedKeys) { Object value = credentialProperties.get(key); if (value == null) { @@ -163,4 +169,61 @@ public Map extractClaimsFromSdJwt(String sdJwtString) { return Collections.emptyMap(); } } + + private LinkedHashMap> buildFallbackDisplayProperties( + Map credentialProperties, + Set orderedKeys, String userLocale) { + + LinkedHashMap> displayProperties = new LinkedHashMap<>(); + + // Use ordered keys from parameter (already includes all fields) + List fieldKeys = new ArrayList<>(orderedKeys); + + // Exclude non-claim metadata + fieldKeys.remove("id"); + + // Build default display entries from claims + for (String key : fieldKeys) { + Object value = credentialProperties.get(key); + if (value == null) { + continue; + } + + CredentialIssuerDisplayResponse display = null; + + // Check if value is a Map containing display information + if (value instanceof Map) { + Map valueMap = (Map) value; + Object displayObj = valueMap.get("display"); + + if (displayObj instanceof List) { + List> displayList = (List>) displayObj; + + if (!displayList.isEmpty()) { + // Try to find matching locale + Optional> matchingDisplay = displayList.stream() + .filter(d -> LocaleUtils.matchesLocale((String) d.get("locale"), userLocale)) + .findFirst(); + + Map selectedDisplay = matchingDisplay.orElse(displayList.get(0)); + + display = new CredentialIssuerDisplayResponse(); + display.setName((String) selectedDisplay.get("name")); + display.setLocale((String) selectedDisplay.get("locale")); + } + } + } + + // Fallback to default display if no nested display found + if (display == null) { + display = new CredentialIssuerDisplayResponse(); + display.setName(camelToTitleCase(key)); + display.setLocale("en"); + } + + displayProperties.put(key, Map.of(display, value)); + } + + return displayProperties; + } } \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/service/LdpVcCredentialFormatHandlerTest.java b/src/test/java/io/mosip/mimoto/service/LdpVcCredentialFormatHandlerTest.java index a8901f206..6c1741762 100644 --- a/src/test/java/io/mosip/mimoto/service/LdpVcCredentialFormatHandlerTest.java +++ b/src/test/java/io/mosip/mimoto/service/LdpVcCredentialFormatHandlerTest.java @@ -140,9 +140,14 @@ void loadDisplayPropertiesFromWellknownWithNullCredentialDefinitionShouldReturnE ldpVcCredentialFormatHandler.loadDisplayPropertiesFromWellknown( credentialProperties, credentialsSupportedResponse, "en"); - // Then + // Then - now returns fallback for firstName assertNotNull(result); - assertTrue(result.isEmpty()); + assertEquals(1, result.size()); + assertTrue(result.containsKey("firstName")); + + CredentialIssuerDisplayResponse display = result.get("firstName").keySet().iterator().next(); + assertEquals("First Name", display.getName()); + assertEquals("en", display.getLocale()); } @Test @@ -159,9 +164,14 @@ void loadDisplayPropertiesFromWellknownWithNullCredentialSubjectShouldReturnEmpt ldpVcCredentialFormatHandler.loadDisplayPropertiesFromWellknown( credentialProperties, credentialsSupportedResponse, "en"); - // Then + // Then - now returns fallback for firstName assertNotNull(result); - assertTrue(result.isEmpty()); + assertEquals(1, result.size()); + assertTrue(result.containsKey("firstName")); + + CredentialIssuerDisplayResponse display = result.get("firstName").keySet().iterator().next(); + assertEquals("First Name", display.getName()); + assertEquals("en", display.getLocale()); } @Test @@ -178,9 +188,14 @@ void loadDisplayPropertiesFromWellknownWithNullDisplayConfigMapShouldReturnEmpty ldpVcCredentialFormatHandler.loadDisplayPropertiesFromWellknown( credentialProperties, credentialsSupportedResponse, "en"); - // Then + // Then - now returns fallback for firstName assertNotNull(result); - assertTrue(result.isEmpty()); + assertEquals(1, result.size()); + assertTrue(result.containsKey("firstName")); + + CredentialIssuerDisplayResponse display = result.get("firstName").keySet().iterator().next(); + assertEquals("First Name", display.getName()); + assertEquals("en", display.getLocale()); } @Test @@ -485,4 +500,210 @@ void loadDisplayPropertiesFromWellknownWithFullNameScenario() { assertEquals("en", mobileDisplay.getLocale()); } } + + @Test + void buildFallbackDisplayPropertiesWithOrderedKeysShouldRespectOrder() { + // Given + Map credentialProperties = new HashMap<>(); + credentialProperties.put("firstName", "John"); + credentialProperties.put("lastName", "Doe"); + credentialProperties.put("email", "john@example.com"); + + List orderedKeys = Arrays.asList("email", "lastName", "firstName"); + credentialsSupportedResponse.setOrder(orderedKeys); + credentialsSupportedResponse.setCredentialDefinition(null); // Trigger fallback + + // When + LinkedHashMap> result = + ldpVcCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "en"); + + // Then + assertNotNull(result); + assertEquals(3, result.size()); + + // Verify order is preserved + List resultKeys = new ArrayList<>(result.keySet()); + assertEquals("email", resultKeys.get(0)); + assertEquals("lastName", resultKeys.get(1)); + assertEquals("firstName", resultKeys.get(2)); + } + + @Test + void buildFallbackDisplayPropertiesWithNestedDisplayInfoShouldUseIt() { + // Given + Map credentialProperties = new HashMap<>(); + + // Create nested display structure + Map addressValue = new HashMap<>(); + List> displayList = new ArrayList<>(); + Map display = new HashMap<>(); + display.put("name", "Home Address"); + display.put("locale", "en"); + displayList.add(display); + addressValue.put("display", displayList); + + credentialProperties.put("address", addressValue); + credentialProperties.put("firstName", "John"); + + credentialsSupportedResponse.setCredentialDefinition(null); // Trigger fallback + + // When + LinkedHashMap> result = + ldpVcCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "en"); + + // Then + assertNotNull(result); + assertEquals(2, result.size()); + + // Verify nested display was used + CredentialIssuerDisplayResponse addressDisplay = result.get("address").keySet().iterator().next(); + assertEquals("Home Address", addressDisplay.getName()); + assertEquals("en", addressDisplay.getLocale()); + + // Verify standard fallback for firstName + CredentialIssuerDisplayResponse firstNameDisplay = result.get("firstName").keySet().iterator().next(); + assertEquals("First Name", firstNameDisplay.getName()); + assertEquals("en", firstNameDisplay.getLocale()); + } + + @Test + void buildFallbackDisplayPropertiesWithLocaleMatchingShouldUseMatchingLocale() { + // Given + Map credentialProperties = new HashMap<>(); + + // Create nested display with multiple locales + Map cityValue = new HashMap<>(); + List> displayList = new ArrayList<>(); + + Map enDisplay = new HashMap<>(); + enDisplay.put("name", "City"); + enDisplay.put("locale", "en"); + displayList.add(enDisplay); + + Map frDisplay = new HashMap<>(); + frDisplay.put("name", "Ville"); + frDisplay.put("locale", "fr"); + displayList.add(frDisplay); + + cityValue.put("display", displayList); + credentialProperties.put("city", cityValue); + + credentialsSupportedResponse.setCredentialDefinition(null); + + try (MockedStatic mockedLocaleUtils = mockStatic(LocaleUtils.class)) { + mockedLocaleUtils.when(() -> LocaleUtils.matchesLocale("fr", "fr")) + .thenReturn(true); + mockedLocaleUtils.when(() -> LocaleUtils.matchesLocale("en", "fr")) + .thenReturn(false); + + // When + LinkedHashMap> result = + ldpVcCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "fr"); + + // Then + assertNotNull(result); + assertEquals(1, result.size()); + + CredentialIssuerDisplayResponse cityDisplay = result.get("city").keySet().iterator().next(); + assertEquals("Ville", cityDisplay.getName()); + assertEquals("fr", cityDisplay.getLocale()); + } + } + + @Test + void buildFallbackDisplayPropertiesWithEmptyDisplayListShouldUseDefaultFallback() { + // Given + Map credentialProperties = new HashMap<>(); + + Map countryValue = new HashMap<>(); + countryValue.put("display", new ArrayList<>()); // Empty display list + credentialProperties.put("country", countryValue); + + credentialsSupportedResponse.setCredentialDefinition(null); + + // When + LinkedHashMap> result = + ldpVcCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "en"); + + // Then + assertNotNull(result); + assertEquals(1, result.size()); + + CredentialIssuerDisplayResponse countryDisplay = result.get("country").keySet().iterator().next(); + assertEquals("Country", countryDisplay.getName()); // Fallback to camelToTitleCase + assertEquals("en", countryDisplay.getLocale()); + } + + @Test + void buildFallbackDisplayPropertiesWithNullValuesShouldSkipFields() { + // Given + Map credentialProperties = new HashMap<>(); + credentialProperties.put("firstName", "John"); + credentialProperties.put("middleName", null); + credentialProperties.put("lastName", "Doe"); + + credentialsSupportedResponse.setCredentialDefinition(null); + + // When + LinkedHashMap> result = + ldpVcCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "en"); + + // Then + assertNotNull(result); + assertEquals(2, result.size()); // middleName should be skipped + assertTrue(result.containsKey("firstName")); + assertTrue(result.containsKey("lastName")); + assertFalse(result.containsKey("middleName")); + } + + @Test + void buildFallbackDisplayPropertiesWithIdFieldShouldExcludeIt() { + // Given + Map credentialProperties = new HashMap<>(); + credentialProperties.put("id", "12345"); + credentialProperties.put("firstName", "John"); + + credentialsSupportedResponse.setCredentialDefinition(null); + + // When + LinkedHashMap> result = + ldpVcCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "en"); + + // Then + assertNotNull(result); + assertEquals(1, result.size()); + assertFalse(result.containsKey("id")); // id should be excluded + assertTrue(result.containsKey("firstName")); + } + + @Test + void buildFallbackDisplayPropertiesWithNonMapDisplayValueShouldUseDefaultFallback() { + // Given + Map credentialProperties = new HashMap<>(); + + Map ageValue = new HashMap<>(); + ageValue.put("display", "Invalid Display Value"); // Not a List + credentialProperties.put("age", ageValue); + + credentialsSupportedResponse.setCredentialDefinition(null); + + // When + LinkedHashMap> result = + ldpVcCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "en"); + + // Then + assertNotNull(result); + assertEquals(1, result.size()); + + CredentialIssuerDisplayResponse ageDisplay = result.get("age").keySet().iterator().next(); + assertEquals("Age", ageDisplay.getName()); // Fallback to camelToTitleCase + assertEquals("en", ageDisplay.getLocale()); + } } diff --git a/src/test/java/io/mosip/mimoto/service/VcSdJwtCredentialFormatHandlerTest.java b/src/test/java/io/mosip/mimoto/service/VcSdJwtCredentialFormatHandlerTest.java index be4238fe8..813576566 100644 --- a/src/test/java/io/mosip/mimoto/service/VcSdJwtCredentialFormatHandlerTest.java +++ b/src/test/java/io/mosip/mimoto/service/VcSdJwtCredentialFormatHandlerTest.java @@ -454,6 +454,239 @@ void getSupportedFormatShouldReturnCorrectFormat() { assertEquals(CredentialFormat.VC_SD_JWT.getFormat(), result); } + @Test + void buildFallbackDisplayPropertiesWithOrderedKeysShouldRespectOrder() { + // Given + Map credentialProperties = new HashMap<>(); + credentialProperties.put("firstName", "John"); + credentialProperties.put("lastName", "Doe"); + credentialProperties.put("email", "john@example.com"); + + List orderedKeys = Arrays.asList("email", "lastName", "firstName"); + credentialsSupportedResponse.setOrder(orderedKeys); + credentialsSupportedResponse.setClaims(null); // Trigger fallback + + // When + LinkedHashMap> result = + vcSdJwtCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "en"); + + // Then + assertNotNull(result); + assertEquals(3, result.size()); + + // Verify order is preserved + List resultKeys = new ArrayList<>(result.keySet()); + assertEquals("email", resultKeys.get(0)); + assertEquals("lastName", resultKeys.get(1)); + assertEquals("firstName", resultKeys.get(2)); + } + + @Test + void buildFallbackDisplayPropertiesWithNestedDisplayInfoShouldUseIt() { + // Given + Map credentialProperties = new HashMap<>(); + + // Create nested display structure + Map addressValue = new HashMap<>(); + List> displayList = new ArrayList<>(); + Map display = new HashMap<>(); + display.put("name", "Home Address"); + display.put("locale", "en"); + displayList.add(display); + addressValue.put("display", displayList); + + credentialProperties.put("address", addressValue); + credentialProperties.put("firstName", "John"); + + credentialsSupportedResponse.setClaims(null); // Trigger fallback + + // When + LinkedHashMap> result = + vcSdJwtCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "en"); + + // Then + assertNotNull(result); + assertEquals(2, result.size()); + + // Verify nested display was used + CredentialIssuerDisplayResponse addressDisplay = result.get("address").keySet().iterator().next(); + assertEquals("Home Address", addressDisplay.getName()); + assertEquals("en", addressDisplay.getLocale()); + + // Verify standard fallback for firstName + CredentialIssuerDisplayResponse firstNameDisplay = result.get("firstName").keySet().iterator().next(); + assertEquals("First Name", firstNameDisplay.getName()); + assertEquals("en", firstNameDisplay.getLocale()); + } + + @Test + void buildFallbackDisplayPropertiesWithLocaleMatchingShouldUseMatchingLocale() { + // Given + Map credentialProperties = new HashMap<>(); + + // Create nested display with multiple locales + Map cityValue = new HashMap<>(); + List> displayList = new ArrayList<>(); + + Map enDisplay = new HashMap<>(); + enDisplay.put("name", "City"); + enDisplay.put("locale", "en"); + displayList.add(enDisplay); + + Map frDisplay = new HashMap<>(); + frDisplay.put("name", "Ville"); + frDisplay.put("locale", "fr"); + displayList.add(frDisplay); + + cityValue.put("display", displayList); + credentialProperties.put("city", cityValue); + + credentialsSupportedResponse.setClaims(null); + + try (MockedStatic mockedLocaleUtils = mockStatic(LocaleUtils.class)) { + mockedLocaleUtils.when(() -> LocaleUtils.matchesLocale("fr", "fr")) + .thenReturn(true); + mockedLocaleUtils.when(() -> LocaleUtils.matchesLocale("en", "fr")) + .thenReturn(false); + + // When + LinkedHashMap> result = + vcSdJwtCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "fr"); + + // Then + assertNotNull(result); + assertEquals(1, result.size()); + + CredentialIssuerDisplayResponse cityDisplay = result.get("city").keySet().iterator().next(); + assertEquals("Ville", cityDisplay.getName()); + assertEquals("fr", cityDisplay.getLocale()); + } + } + + @Test + void buildFallbackDisplayPropertiesWithEmptyDisplayListShouldUseDefaultFallback() { + // Given + Map credentialProperties = new HashMap<>(); + + Map countryValue = new HashMap<>(); + countryValue.put("display", new ArrayList<>()); // Empty display list + credentialProperties.put("country", countryValue); + + credentialsSupportedResponse.setClaims(null); + + // When + LinkedHashMap> result = + vcSdJwtCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "en"); + + // Then + assertNotNull(result); + assertEquals(1, result.size()); + + CredentialIssuerDisplayResponse countryDisplay = result.get("country").keySet().iterator().next(); + assertEquals("Country", countryDisplay.getName()); // Fallback to camelToTitleCase + assertEquals("en", countryDisplay.getLocale()); + } + + @Test + void buildFallbackDisplayPropertiesWithNullValuesShouldSkipFields() { + // Given + Map credentialProperties = new HashMap<>(); + credentialProperties.put("firstName", "John"); + credentialProperties.put("middleName", null); + credentialProperties.put("lastName", "Doe"); + + credentialsSupportedResponse.setClaims(null); + + // When + LinkedHashMap> result = + vcSdJwtCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "en"); + + // Then + assertNotNull(result); + assertEquals(2, result.size()); // middleName should be skipped + assertTrue(result.containsKey("firstName")); + assertTrue(result.containsKey("lastName")); + assertFalse(result.containsKey("middleName")); + } + + @Test + void buildFallbackDisplayPropertiesWithIdFieldShouldExcludeIt() { + // Given + Map credentialProperties = new HashMap<>(); + credentialProperties.put("id", "12345"); + credentialProperties.put("firstName", "John"); + + credentialsSupportedResponse.setClaims(null); + + // When + LinkedHashMap> result = + vcSdJwtCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "en"); + + // Then + assertNotNull(result); + assertEquals(1, result.size()); + assertFalse(result.containsKey("id")); // id should be excluded + assertTrue(result.containsKey("firstName")); + } + + @Test + void buildFallbackDisplayPropertiesWithNonMapDisplayValueShouldUseDefaultFallback() { + // Given + Map credentialProperties = new HashMap<>(); + + Map ageValue = new HashMap<>(); + ageValue.put("display", "Invalid Display Value"); // Not a List + credentialProperties.put("age", ageValue); + + credentialsSupportedResponse.setClaims(null); + + // When + LinkedHashMap> result = + vcSdJwtCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "en"); + + // Then + assertNotNull(result); + assertEquals(1, result.size()); + + CredentialIssuerDisplayResponse ageDisplay = result.get("age").keySet().iterator().next(); + assertEquals("Age", ageDisplay.getName()); // Fallback to camelToTitleCase + assertEquals("en", ageDisplay.getLocale()); + } + + @Test + void buildFallbackDisplayPropertiesWithEmptyClaimsShouldTriggerFallback() { + // Given + Map credentialProperties = new HashMap<>(); + credentialProperties.put("firstName", "John"); + credentialProperties.put("lastName", "Doe"); + + credentialsSupportedResponse.setClaims(new HashMap<>()); // Empty claims + + // When + LinkedHashMap> result = + vcSdJwtCredentialFormatHandler.loadDisplayPropertiesFromWellknown( + credentialProperties, credentialsSupportedResponse, "en"); + + // Then + assertNotNull(result); + assertEquals(2, result.size()); + + CredentialIssuerDisplayResponse firstNameDisplay = result.get("firstName").keySet().iterator().next(); + assertEquals("First Name", firstNameDisplay.getName()); + assertEquals("en", firstNameDisplay.getLocale()); + + CredentialIssuerDisplayResponse lastNameDisplay = result.get("lastName").keySet().iterator().next(); + assertEquals("Last Name", lastNameDisplay.getName()); + assertEquals("en", lastNameDisplay.getLocale()); + } + // Helper methods private Map createSampleClaims() { Map claims = new HashMap<>(); diff --git a/src/test/java/io/mosip/mimoto/util/CredentialIssuerWellknownResponseValidatorTest.java b/src/test/java/io/mosip/mimoto/util/CredentialIssuerWellknownResponseValidatorTest.java index 988d6ab58..f17db94c4 100644 --- a/src/test/java/io/mosip/mimoto/util/CredentialIssuerWellknownResponseValidatorTest.java +++ b/src/test/java/io/mosip/mimoto/util/CredentialIssuerWellknownResponseValidatorTest.java @@ -282,11 +282,10 @@ void shouldDetectMissingMandatoryFieldsOfCredentialDefinitionInWellknownResponse credentialIssuerWellknownResponseValidator.validate(response, validator) ); - assertTrue(Arrays.stream(invalidWellknownResponseException.getMessage().split("\n")).toList().containsAll(Arrays.stream(""" - RESIDENT-APP-041 --> Invalid Wellknown from Issuer - Validation failed: - type: must not be empty - credentialSubject: must not be empty""".split("\n")).toList())); + // Update to check message contains validation errors + String message = invalidWellknownResponseException.getMessage(); + assertTrue(message.contains("RESIDENT-APP-041 --> Invalid Wellknown from Issuer")); + assertTrue(message.contains("type: must not be empty")); } @Test From 7fa7b0038b8e15ac91e36c2ed1d033b273aac039 Mon Sep 17 00:00:00 2001 From: cyber-titan Date: Fri, 7 Nov 2025 08:31:47 +0530 Subject: [PATCH 03/12] injiweb-1671-credSub-optional removed display credSub logic Signed-off-by: cyber-titan --- .../impl/LdpVcCredentialFormatHandler.java | 42 +----- .../impl/VcSdJwtCredentialFormatHandler.java | 47 +----- .../LdpVcCredentialFormatHandlerTest.java | 134 ----------------- .../VcSdJwtCredentialFormatHandlerTest.java | 139 +----------------- 4 files changed, 17 insertions(+), 345 deletions(-) diff --git a/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java b/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java index c108a9047..7aca5629b 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java +++ b/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java @@ -80,7 +80,7 @@ public LinkedHashMap> loadD if (credentialsSupportedResponse.getCredentialDefinition() == null || credentialsSupportedResponse.getCredentialDefinition().getCredentialSubject() == null) { log.info("Issuer well-known has no credential definition or credential subject for LDP VC format; falling back to claim-based display properties"); - return buildFallbackDisplayProperties(credentialProperties, orderedKeys, userLocale); + return buildFallbackDisplayProperties(credentialProperties, orderedKeys); } Map displayConfigMap = @@ -88,7 +88,7 @@ public LinkedHashMap> loadD if (displayConfigMap == null) { log.info("No display configuration found in issuer well-known for LDP VC format; falling back to claim-based display properties"); - return buildFallbackDisplayProperties(credentialProperties, orderedKeys, userLocale); + return buildFallbackDisplayProperties(credentialProperties, orderedKeys); } String resolvedLocale = LocaleUtils.resolveLocaleWithFallback(displayConfigMap, userLocale); @@ -123,7 +123,7 @@ public LinkedHashMap> loadD private LinkedHashMap> buildFallbackDisplayProperties( Map credentialProperties, - List orderedKeys, String userLocale) { + List orderedKeys) { LinkedHashMap> displayProperties = new LinkedHashMap<>(); @@ -142,41 +142,13 @@ private LinkedHashMap> buil continue; } - CredentialIssuerDisplayResponse display = null; - - // Check if value is a Map containing display information - if (value instanceof Map) { - Map valueMap = (Map) value; - Object displayObj = valueMap.get("display"); - - if (displayObj instanceof List) { - List> displayList = (List>) displayObj; - - if (!displayList.isEmpty()) { - // Try to find matching locale - Optional> matchingDisplay = displayList.stream() - .filter(d -> LocaleUtils.matchesLocale((String) d.get("locale"), userLocale)) - .findFirst(); - - Map selectedDisplay = matchingDisplay.orElse(displayList.get(0)); - - display = new CredentialIssuerDisplayResponse(); - display.setName((String) selectedDisplay.get("name")); - display.setLocale((String) selectedDisplay.get("locale")); - } - } - } - - // Fallback to default display if no nested display found - if (display == null) { - display = new CredentialIssuerDisplayResponse(); - display.setName(camelToTitleCase(key)); - display.setLocale("en"); - } + // Generate fallback display using vc keys + CredentialIssuerDisplayResponse display = new CredentialIssuerDisplayResponse(); + display.setName(camelToTitleCase(key)); + display.setLocale("en"); displayProperties.put(key, Map.of(display, value)); } - return displayProperties; } } diff --git a/src/main/java/io/mosip/mimoto/service/impl/VcSdJwtCredentialFormatHandler.java b/src/main/java/io/mosip/mimoto/service/impl/VcSdJwtCredentialFormatHandler.java index 04f2473c3..27c332744 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/VcSdJwtCredentialFormatHandler.java +++ b/src/main/java/io/mosip/mimoto/service/impl/VcSdJwtCredentialFormatHandler.java @@ -67,7 +67,7 @@ public LinkedHashMap> loadD if (credentialsSupportedResponse.getClaims() == null || credentialsSupportedResponse.getClaims().isEmpty()) { log.info("Issuer well-known has no claims for SD-JWT format; falling back to claim-based display properties"); - return buildFallbackDisplayProperties(credentialProperties, orderedKeys, userLocale); + return buildFallbackDisplayProperties(credentialProperties, orderedKeys); } // Extract raw claims and convert to DTOs @@ -85,7 +85,7 @@ public LinkedHashMap> loadD if (convertedClaimsMap.isEmpty()) { log.info("No display configuration found for SD-JWT format"); - return buildFallbackDisplayProperties(credentialProperties, orderedKeys, userLocale); + return buildFallbackDisplayProperties(credentialProperties, orderedKeys); } String resolvedLocale = LocaleUtils.resolveLocaleWithFallback(convertedClaimsMap, userLocale); @@ -126,7 +126,7 @@ public LinkedHashMap> loadD public Map extractClaimsFromSdJwt(String sdJwtString) { try { SDJWT sdJwt = SDJWT.parse(sdJwtString); - Map claims = new HashMap<>(); + Map claims = new LinkedHashMap<>(); // Parse JWT payload String credentialJwt = sdJwt.getCredentialJwt(); @@ -172,16 +172,13 @@ public Map extractClaimsFromSdJwt(String sdJwtString) { private LinkedHashMap> buildFallbackDisplayProperties( Map credentialProperties, - Set orderedKeys, String userLocale) { + Set orderedKeys) { LinkedHashMap> displayProperties = new LinkedHashMap<>(); // Use ordered keys from parameter (already includes all fields) List fieldKeys = new ArrayList<>(orderedKeys); - // Exclude non-claim metadata - fieldKeys.remove("id"); - // Build default display entries from claims for (String key : fieldKeys) { Object value = credentialProperties.get(key); @@ -189,41 +186,13 @@ private LinkedHashMap> buil continue; } - CredentialIssuerDisplayResponse display = null; - - // Check if value is a Map containing display information - if (value instanceof Map) { - Map valueMap = (Map) value; - Object displayObj = valueMap.get("display"); - - if (displayObj instanceof List) { - List> displayList = (List>) displayObj; - - if (!displayList.isEmpty()) { - // Try to find matching locale - Optional> matchingDisplay = displayList.stream() - .filter(d -> LocaleUtils.matchesLocale((String) d.get("locale"), userLocale)) - .findFirst(); - - Map selectedDisplay = matchingDisplay.orElse(displayList.get(0)); - - display = new CredentialIssuerDisplayResponse(); - display.setName((String) selectedDisplay.get("name")); - display.setLocale((String) selectedDisplay.get("locale")); - } - } - } - - // Fallback to default display if no nested display found - if (display == null) { - display = new CredentialIssuerDisplayResponse(); - display.setName(camelToTitleCase(key)); - display.setLocale("en"); - } + // Generate fallback display using claims keys + CredentialIssuerDisplayResponse display = new CredentialIssuerDisplayResponse(); + display.setName(camelToTitleCase(key)); + display.setLocale("en"); displayProperties.put(key, Map.of(display, value)); } - return displayProperties; } } \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/service/LdpVcCredentialFormatHandlerTest.java b/src/test/java/io/mosip/mimoto/service/LdpVcCredentialFormatHandlerTest.java index 6c1741762..371f46fa9 100644 --- a/src/test/java/io/mosip/mimoto/service/LdpVcCredentialFormatHandlerTest.java +++ b/src/test/java/io/mosip/mimoto/service/LdpVcCredentialFormatHandlerTest.java @@ -529,115 +529,6 @@ void buildFallbackDisplayPropertiesWithOrderedKeysShouldRespectOrder() { assertEquals("firstName", resultKeys.get(2)); } - @Test - void buildFallbackDisplayPropertiesWithNestedDisplayInfoShouldUseIt() { - // Given - Map credentialProperties = new HashMap<>(); - - // Create nested display structure - Map addressValue = new HashMap<>(); - List> displayList = new ArrayList<>(); - Map display = new HashMap<>(); - display.put("name", "Home Address"); - display.put("locale", "en"); - displayList.add(display); - addressValue.put("display", displayList); - - credentialProperties.put("address", addressValue); - credentialProperties.put("firstName", "John"); - - credentialsSupportedResponse.setCredentialDefinition(null); // Trigger fallback - - // When - LinkedHashMap> result = - ldpVcCredentialFormatHandler.loadDisplayPropertiesFromWellknown( - credentialProperties, credentialsSupportedResponse, "en"); - - // Then - assertNotNull(result); - assertEquals(2, result.size()); - - // Verify nested display was used - CredentialIssuerDisplayResponse addressDisplay = result.get("address").keySet().iterator().next(); - assertEquals("Home Address", addressDisplay.getName()); - assertEquals("en", addressDisplay.getLocale()); - - // Verify standard fallback for firstName - CredentialIssuerDisplayResponse firstNameDisplay = result.get("firstName").keySet().iterator().next(); - assertEquals("First Name", firstNameDisplay.getName()); - assertEquals("en", firstNameDisplay.getLocale()); - } - - @Test - void buildFallbackDisplayPropertiesWithLocaleMatchingShouldUseMatchingLocale() { - // Given - Map credentialProperties = new HashMap<>(); - - // Create nested display with multiple locales - Map cityValue = new HashMap<>(); - List> displayList = new ArrayList<>(); - - Map enDisplay = new HashMap<>(); - enDisplay.put("name", "City"); - enDisplay.put("locale", "en"); - displayList.add(enDisplay); - - Map frDisplay = new HashMap<>(); - frDisplay.put("name", "Ville"); - frDisplay.put("locale", "fr"); - displayList.add(frDisplay); - - cityValue.put("display", displayList); - credentialProperties.put("city", cityValue); - - credentialsSupportedResponse.setCredentialDefinition(null); - - try (MockedStatic mockedLocaleUtils = mockStatic(LocaleUtils.class)) { - mockedLocaleUtils.when(() -> LocaleUtils.matchesLocale("fr", "fr")) - .thenReturn(true); - mockedLocaleUtils.when(() -> LocaleUtils.matchesLocale("en", "fr")) - .thenReturn(false); - - // When - LinkedHashMap> result = - ldpVcCredentialFormatHandler.loadDisplayPropertiesFromWellknown( - credentialProperties, credentialsSupportedResponse, "fr"); - - // Then - assertNotNull(result); - assertEquals(1, result.size()); - - CredentialIssuerDisplayResponse cityDisplay = result.get("city").keySet().iterator().next(); - assertEquals("Ville", cityDisplay.getName()); - assertEquals("fr", cityDisplay.getLocale()); - } - } - - @Test - void buildFallbackDisplayPropertiesWithEmptyDisplayListShouldUseDefaultFallback() { - // Given - Map credentialProperties = new HashMap<>(); - - Map countryValue = new HashMap<>(); - countryValue.put("display", new ArrayList<>()); // Empty display list - credentialProperties.put("country", countryValue); - - credentialsSupportedResponse.setCredentialDefinition(null); - - // When - LinkedHashMap> result = - ldpVcCredentialFormatHandler.loadDisplayPropertiesFromWellknown( - credentialProperties, credentialsSupportedResponse, "en"); - - // Then - assertNotNull(result); - assertEquals(1, result.size()); - - CredentialIssuerDisplayResponse countryDisplay = result.get("country").keySet().iterator().next(); - assertEquals("Country", countryDisplay.getName()); // Fallback to camelToTitleCase - assertEquals("en", countryDisplay.getLocale()); - } - @Test void buildFallbackDisplayPropertiesWithNullValuesShouldSkipFields() { // Given @@ -681,29 +572,4 @@ void buildFallbackDisplayPropertiesWithIdFieldShouldExcludeIt() { assertFalse(result.containsKey("id")); // id should be excluded assertTrue(result.containsKey("firstName")); } - - @Test - void buildFallbackDisplayPropertiesWithNonMapDisplayValueShouldUseDefaultFallback() { - // Given - Map credentialProperties = new HashMap<>(); - - Map ageValue = new HashMap<>(); - ageValue.put("display", "Invalid Display Value"); // Not a List - credentialProperties.put("age", ageValue); - - credentialsSupportedResponse.setCredentialDefinition(null); - - // When - LinkedHashMap> result = - ldpVcCredentialFormatHandler.loadDisplayPropertiesFromWellknown( - credentialProperties, credentialsSupportedResponse, "en"); - - // Then - assertNotNull(result); - assertEquals(1, result.size()); - - CredentialIssuerDisplayResponse ageDisplay = result.get("age").keySet().iterator().next(); - assertEquals("Age", ageDisplay.getName()); // Fallback to camelToTitleCase - assertEquals("en", ageDisplay.getLocale()); - } } diff --git a/src/test/java/io/mosip/mimoto/service/VcSdJwtCredentialFormatHandlerTest.java b/src/test/java/io/mosip/mimoto/service/VcSdJwtCredentialFormatHandlerTest.java index 813576566..a78b5ea7f 100644 --- a/src/test/java/io/mosip/mimoto/service/VcSdJwtCredentialFormatHandlerTest.java +++ b/src/test/java/io/mosip/mimoto/service/VcSdJwtCredentialFormatHandlerTest.java @@ -482,115 +482,6 @@ void buildFallbackDisplayPropertiesWithOrderedKeysShouldRespectOrder() { assertEquals("firstName", resultKeys.get(2)); } - @Test - void buildFallbackDisplayPropertiesWithNestedDisplayInfoShouldUseIt() { - // Given - Map credentialProperties = new HashMap<>(); - - // Create nested display structure - Map addressValue = new HashMap<>(); - List> displayList = new ArrayList<>(); - Map display = new HashMap<>(); - display.put("name", "Home Address"); - display.put("locale", "en"); - displayList.add(display); - addressValue.put("display", displayList); - - credentialProperties.put("address", addressValue); - credentialProperties.put("firstName", "John"); - - credentialsSupportedResponse.setClaims(null); // Trigger fallback - - // When - LinkedHashMap> result = - vcSdJwtCredentialFormatHandler.loadDisplayPropertiesFromWellknown( - credentialProperties, credentialsSupportedResponse, "en"); - - // Then - assertNotNull(result); - assertEquals(2, result.size()); - - // Verify nested display was used - CredentialIssuerDisplayResponse addressDisplay = result.get("address").keySet().iterator().next(); - assertEquals("Home Address", addressDisplay.getName()); - assertEquals("en", addressDisplay.getLocale()); - - // Verify standard fallback for firstName - CredentialIssuerDisplayResponse firstNameDisplay = result.get("firstName").keySet().iterator().next(); - assertEquals("First Name", firstNameDisplay.getName()); - assertEquals("en", firstNameDisplay.getLocale()); - } - - @Test - void buildFallbackDisplayPropertiesWithLocaleMatchingShouldUseMatchingLocale() { - // Given - Map credentialProperties = new HashMap<>(); - - // Create nested display with multiple locales - Map cityValue = new HashMap<>(); - List> displayList = new ArrayList<>(); - - Map enDisplay = new HashMap<>(); - enDisplay.put("name", "City"); - enDisplay.put("locale", "en"); - displayList.add(enDisplay); - - Map frDisplay = new HashMap<>(); - frDisplay.put("name", "Ville"); - frDisplay.put("locale", "fr"); - displayList.add(frDisplay); - - cityValue.put("display", displayList); - credentialProperties.put("city", cityValue); - - credentialsSupportedResponse.setClaims(null); - - try (MockedStatic mockedLocaleUtils = mockStatic(LocaleUtils.class)) { - mockedLocaleUtils.when(() -> LocaleUtils.matchesLocale("fr", "fr")) - .thenReturn(true); - mockedLocaleUtils.when(() -> LocaleUtils.matchesLocale("en", "fr")) - .thenReturn(false); - - // When - LinkedHashMap> result = - vcSdJwtCredentialFormatHandler.loadDisplayPropertiesFromWellknown( - credentialProperties, credentialsSupportedResponse, "fr"); - - // Then - assertNotNull(result); - assertEquals(1, result.size()); - - CredentialIssuerDisplayResponse cityDisplay = result.get("city").keySet().iterator().next(); - assertEquals("Ville", cityDisplay.getName()); - assertEquals("fr", cityDisplay.getLocale()); - } - } - - @Test - void buildFallbackDisplayPropertiesWithEmptyDisplayListShouldUseDefaultFallback() { - // Given - Map credentialProperties = new HashMap<>(); - - Map countryValue = new HashMap<>(); - countryValue.put("display", new ArrayList<>()); // Empty display list - credentialProperties.put("country", countryValue); - - credentialsSupportedResponse.setClaims(null); - - // When - LinkedHashMap> result = - vcSdJwtCredentialFormatHandler.loadDisplayPropertiesFromWellknown( - credentialProperties, credentialsSupportedResponse, "en"); - - // Then - assertNotNull(result); - assertEquals(1, result.size()); - - CredentialIssuerDisplayResponse countryDisplay = result.get("country").keySet().iterator().next(); - assertEquals("Country", countryDisplay.getName()); // Fallback to camelToTitleCase - assertEquals("en", countryDisplay.getLocale()); - } - @Test void buildFallbackDisplayPropertiesWithNullValuesShouldSkipFields() { // Given @@ -617,7 +508,7 @@ void buildFallbackDisplayPropertiesWithNullValuesShouldSkipFields() { @Test void buildFallbackDisplayPropertiesWithIdFieldShouldExcludeIt() { // Given - Map credentialProperties = new HashMap<>(); + Map credentialProperties = new LinkedHashMap<>(); credentialProperties.put("id", "12345"); credentialProperties.put("firstName", "John"); @@ -630,36 +521,10 @@ void buildFallbackDisplayPropertiesWithIdFieldShouldExcludeIt() { // Then assertNotNull(result); - assertEquals(1, result.size()); - assertFalse(result.containsKey("id")); // id should be excluded + assertEquals(2, result.size()); assertTrue(result.containsKey("firstName")); } - @Test - void buildFallbackDisplayPropertiesWithNonMapDisplayValueShouldUseDefaultFallback() { - // Given - Map credentialProperties = new HashMap<>(); - - Map ageValue = new HashMap<>(); - ageValue.put("display", "Invalid Display Value"); // Not a List - credentialProperties.put("age", ageValue); - - credentialsSupportedResponse.setClaims(null); - - // When - LinkedHashMap> result = - vcSdJwtCredentialFormatHandler.loadDisplayPropertiesFromWellknown( - credentialProperties, credentialsSupportedResponse, "en"); - - // Then - assertNotNull(result); - assertEquals(1, result.size()); - - CredentialIssuerDisplayResponse ageDisplay = result.get("age").keySet().iterator().next(); - assertEquals("Age", ageDisplay.getName()); // Fallback to camelToTitleCase - assertEquals("en", ageDisplay.getLocale()); - } - @Test void buildFallbackDisplayPropertiesWithEmptyClaimsShouldTriggerFallback() { // Given From be43bcb89e6976f856fd73590f5b55a7308e4f32 Mon Sep 17 00:00:00 2001 From: cyber-titan Date: Fri, 7 Nov 2025 16:15:13 +0530 Subject: [PATCH 04/12] injiweb-1671-credSub-optional removed id field from sd-jwt Signed-off-by: cyber-titan --- .../mimoto/service/impl/VcSdJwtCredentialFormatHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/mosip/mimoto/service/impl/VcSdJwtCredentialFormatHandler.java b/src/main/java/io/mosip/mimoto/service/impl/VcSdJwtCredentialFormatHandler.java index 27c332744..2cf27c918 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/VcSdJwtCredentialFormatHandler.java +++ b/src/main/java/io/mosip/mimoto/service/impl/VcSdJwtCredentialFormatHandler.java @@ -155,7 +155,7 @@ public Map extractClaimsFromSdJwt(String sdJwtString) { } // Remove standard JWT claims and SD-JWT metadata - List metadataKeys = Arrays.asList("vct", "cnf", "iss", "sub", "aud", "exp", "nbf", "iat", "jti", "_sd", "_sd_alg"); + List metadataKeys = Arrays.asList("vct", "cnf", "iss", "sub", "aud", "exp", "nbf", "iat", "jti", "_sd", "_sd_alg", "id"); metadataKeys.forEach(claims::remove); // Return claims directly as result From 4074267d7407ef6a64a45789a8de75266c92e7a1 Mon Sep 17 00:00:00 2001 From: Durgesh Konga Date: Thu, 6 Nov 2025 13:59:31 +0530 Subject: [PATCH 05/12] [INJIWEB-1746] - Fix direct_post response mode: use form-encoded data and update redirect URI handling (#962) * Fix direct_post response mode: use form-encoded data and update redirect URI handling Signed-off-by: kongadurgesh * [INJIWEB-1746] - Update logic to null check redirect_uri Update Test case for empty redirect_uri Signed-off-by: kongadurgesh --------- Signed-off-by: kongadurgesh --- .../PresentationDefinitionDTO.java | 2 +- .../service/impl/PresentationServiceImpl.java | 36 ++++++++++++------- .../service/PresentationServiceTest.java | 4 +-- .../io/mosip/mimoto/util/TestUtilities.java | 9 +++++ 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/mosip/mimoto/dto/openid/presentation/PresentationDefinitionDTO.java b/src/main/java/io/mosip/mimoto/dto/openid/presentation/PresentationDefinitionDTO.java index 0f3a86dbe..eed13571c 100644 --- a/src/main/java/io/mosip/mimoto/dto/openid/presentation/PresentationDefinitionDTO.java +++ b/src/main/java/io/mosip/mimoto/dto/openid/presentation/PresentationDefinitionDTO.java @@ -15,6 +15,6 @@ public class PresentationDefinitionDTO { String id; - @JsonProperty("inputDescriptors") + @JsonProperty("input_descriptors") List inputDescriptors; } diff --git a/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java b/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java index ca939bffe..44b260d84 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java +++ b/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java @@ -29,6 +29,8 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import java.io.IOException; import java.net.URISyntaxException; @@ -168,6 +170,7 @@ private String processInputDescriptor(VCCredentialResponse vcCredentialResponse, // Create VP Token String vpToken = createVpToken(vpDTO); + // Create PresentationSubmission String presentationSubmission = constructPresentationSubmission(format, vpDTO, presentationDefinitionDTO, inputDescriptorDTO); @@ -175,6 +178,7 @@ private String processInputDescriptor(VCCredentialResponse vcCredentialResponse, if (presentationRequestDTO.getResponseMode() != null && "direct_post".equals(presentationRequestDTO.getResponseMode())) { return postVpToResponseUri( presentationRequestDTO.getResponseUri(), + presentationRequestDTO.getRedirectUri(), vpToken, presentationSubmission, presentationRequestDTO.getState(), @@ -207,33 +211,39 @@ private String buildRedirectString(String vpToken, String redirectUri, String pr URLEncoder.encode(presentationSubmission, StandardCharsets.UTF_8)); } - private String postVpToResponseUri(String responseUri, String vpToken, String presentationSubmission, String state, String nonce) throws JsonProcessingException { - Map postRequest = new HashMap<>(); - postRequest.put("vp_token", vpToken); - postRequest.put("presentation_submission", objectMapper.readTree(presentationSubmission)); + private String postVpToResponseUri(String responseUri, String redirectUri, String vpToken, String presentationSubmission, String state, String nonce) throws JsonProcessingException { + MultiValueMap postRequest = new LinkedMultiValueMap<>(); + postRequest.add("vp_token", Base64.getUrlEncoder().encodeToString(vpToken.getBytes(StandardCharsets.UTF_8))); + postRequest.add("presentation_submission", presentationSubmission); if (state != null) { - postRequest.put("state", state); - } - - if (nonce != null) { - postRequest.put("nonce", nonce); + postRequest.add("state", state); } log.info("Posting VP to response_uri: {}", responseUri); try { Map postResponse = restApiClient.postApi( responseUri, - MediaType.APPLICATION_JSON, + MediaType.APPLICATION_FORM_URLENCODED, postRequest, Map.class ); log.info("Response from verifier after POST: {}", postResponse); - // Check for redirect_uri in response - String redirectUri = (String) postResponse.get("redirect_uri"); - if (redirectUri != null && !redirectUri.isEmpty()) { + + // Check for redirect_uri in response first + if (postResponse != null && postResponse.containsKey("redirect_uri")) { + String responseRedirectUri = (String) postResponse.get("redirect_uri"); + if (responseRedirectUri != null && !responseRedirectUri.isEmpty()) { + return responseRedirectUri; + } + } + + // Use request's redirectUri if it's non-blank + if (redirectUri != null && !redirectUri.isBlank()) { + log.info("Using redirectUri from request: {}", redirectUri); return redirectUri; } + // Fallback behavior if redirect_uri is not provided log.warn("No redirect_uri received from verifier in POST response. Falling back to response_uri."); return responseUri + "?status=vp_sent"; diff --git a/src/test/java/io/mosip/mimoto/service/PresentationServiceTest.java b/src/test/java/io/mosip/mimoto/service/PresentationServiceTest.java index a3d025867..c517eac44 100644 --- a/src/test/java/io/mosip/mimoto/service/PresentationServiceTest.java +++ b/src/test/java/io/mosip/mimoto/service/PresentationServiceTest.java @@ -482,7 +482,7 @@ public void testDirectPostResponseModeWithoutRedirectUri() throws Exception { String result = presentationService.authorizePresentation(presentationRequestDTO); - assertEquals("https://verifier.example.com/response?status=vp_sent", result); + assertEquals("test_redirect_uri", result); verify(restApiClient).postApi(eq("https://verifier.example.com/response"), any(), any(), eq(Map.class)); } @@ -639,7 +639,7 @@ public void testConstructPresentationDefinitionForSdJwtWithMapTypeNullValue() { @Test public void testDirectPostResponseModeWithEmptyRedirectUri() throws Exception { VCCredentialResponse vcCredentialResponse = TestUtilities.getVCCredentialResponseDTO("Ed25519Signature2020"); - PresentationRequestDTO presentationRequestDTO = TestUtilities.getPresentationRequestDTO(); + PresentationRequestDTO presentationRequestDTO = TestUtilities.getPresentationRequestDTOWithEmptyRedirectURI(); presentationRequestDTO.setResponseMode("direct_post"); presentationRequestDTO.setResponseUri("https://verifier.example.com/response"); presentationRequestDTO.setState("test-state"); diff --git a/src/test/java/io/mosip/mimoto/util/TestUtilities.java b/src/test/java/io/mosip/mimoto/util/TestUtilities.java index 1534ddc9e..73119bf9c 100644 --- a/src/test/java/io/mosip/mimoto/util/TestUtilities.java +++ b/src/test/java/io/mosip/mimoto/util/TestUtilities.java @@ -293,6 +293,15 @@ public static PresentationRequestDTO getPresentationRequestDTO() { .redirectUri("test_redirect_uri").build(); } + public static PresentationRequestDTO getPresentationRequestDTOWithEmptyRedirectURI() { + return PresentationRequestDTO.builder() + .presentationDefinition(getPresentationDefinitionDTO()) + .clientId("test_client_id") + .resource("http://datashare.datashare/v1/datashare/get/static-policyid/static-subscriberid/test") + .responseType("test_response_type") + .redirectUri("").build(); + } + public static VCCredentialProperties getVCCredentialPropertiesDTO(String type) { ArrayList contextList = new ArrayList<>(); From d5f224bf0ae3783b8b68c5af7160833040fbe3ed Mon Sep 17 00:00:00 2001 From: Durgesh Konga Date: Thu, 6 Nov 2025 16:12:08 +0530 Subject: [PATCH 06/12] [INJIWEB-1758] - Upgrade spring boot version for security vulnerabilities (#961) Signed-off-by: kongadurgesh --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0f0c698c8..8447f75ba 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ 3.6.3 - 3.2.3 + 3.3.7 3.4.0 3.4.1 From 4bf3b16bae47309f264fb25b0738ddbf8e9a6708 Mon Sep 17 00:00:00 2001 From: Durgesh Konga Date: Fri, 7 Nov 2025 11:08:42 +0530 Subject: [PATCH 07/12] INJIWEB-1746 - All logs for debug (#964) Signed-off-by: kongadurgesh --- .../io/mosip/mimoto/service/impl/DataShareServiceImpl.java | 2 +- .../mosip/mimoto/service/impl/PresentationServiceImpl.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/mosip/mimoto/service/impl/DataShareServiceImpl.java b/src/main/java/io/mosip/mimoto/service/impl/DataShareServiceImpl.java index 222e6e3b6..936886333 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/DataShareServiceImpl.java +++ b/src/main/java/io/mosip/mimoto/service/impl/DataShareServiceImpl.java @@ -111,7 +111,7 @@ public VCCredentialResponse downloadCredentialFromDataShare(PresentationRequest } VCCredentialResponse vcCredentialResponse = objectMapper.readValue(vcCredentialResponseString, VCCredentialResponse.class); - log.info("Completed Mapping the Credential to Object => "); + log.info("Completed Mapping the Credential to Object => {}", vcCredentialResponse.toString()); if(vcCredentialResponse.getCredential() == null){ DataShareResponseDto dataShareResponse = objectMapper.readValue(vcCredentialResponseString, DataShareResponseDto.class); String errorCode = dataShareResponse.getErrors().get(0).getErrorCode(); diff --git a/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java b/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java index 44b260d84..89acc6d7b 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java +++ b/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java @@ -120,6 +120,7 @@ private List getPreRegisteredVerifiers() throws ApiNotAccessibleExcept @Override public String authorizePresentation(PresentationRequestDTO presentationRequestDTO) throws IOException { VCCredentialResponse vcCredentialResponse = dataShareService.downloadCredentialFromDataShare(presentationRequestDTO); + log.info("Downloaded credential from DataShare for presentation request, {}", vcCredentialResponse.toString()); PresentationDefinitionDTO presentationDefinitionDTO = presentationRequestDTO.getPresentationDefinition(); if (presentationDefinitionDTO == null) { throw new VPNotCreatedException(ErrorConstants.INVALID_REQUEST.getErrorMessage()); @@ -145,26 +146,32 @@ private String processInputDescriptor(VCCredentialResponse vcCredentialResponse, String format = vcCredentialResponse.getFormat(); VerifiablePresentationDTO vpDTO; + log.info("Processing credential format: {}, CredentialFormat.LDP_VC.getFormat() : {}", format, CredentialFormat.LDP_VC.getFormat()); if (CredentialFormat.LDP_VC.getFormat().equalsIgnoreCase(format)) { VCCredentialProperties ldpCredential = objectMapper.convertValue(vcCredentialResponse.getCredential(), VCCredentialProperties.class); if (inputDescriptorDTO.getFormat().get("ldpVc").get("proofTypes") .stream().anyMatch(proofType -> ldpCredential.getProof().getType().equals(proofType))) { + log.info("Credential proof type matched: {}", ldpCredential.getProof().getType()); vpDTO = constructVerifiablePresentationString(ldpCredential); } else { + log.info("Credential proof type did not match. Expected one of: {}", inputDescriptorDTO.getFormat().get("ldpVc").get("proofTypes")); throw new VPNotCreatedException(ErrorConstants.INVALID_REQUEST.getErrorMessage()); } } else if (CredentialFormat.VC_SD_JWT.getFormat().equalsIgnoreCase(format) || CredentialFormat.DC_SD_JWT.getFormat().equalsIgnoreCase(format)) { + log.info("Processing SD-JWT credential"); String credential = objectMapper.convertValue(vcCredentialResponse.getCredential(), String.class); Map jwtHeaders = parseJwtHeader(credential); String responseAlgo = (String) jwtHeaders.get("alg"); if (inputDescriptorDTO.getFormat().get(format).get("sd-jwt_alg_values") .stream().anyMatch(responseAlgo::equals)) { + log.info("SD-JWT algorithm matched: {}", responseAlgo); vpDTO = constructVerifiablePresentationStringForSDjwt(credential); } else { throw new VPNotCreatedException(ErrorConstants.INVALID_REQUEST.getErrorMessage()); } } else { + log.info("Unsupported credential format: {}, CredentialFormat.LDP_VC.getFormat().equalsIgnoreCase(format): {}", format, CredentialFormat.LDP_VC.getFormat().equalsIgnoreCase(format)); throw new VPNotCreatedException(ErrorConstants.INVALID_REQUEST.getErrorMessage()); } From 6842bc4822b0cb51a3c89c036fc532ccd9c66d7f Mon Sep 17 00:00:00 2001 From: Durgesh Konga Date: Fri, 7 Nov 2025 18:27:22 +0530 Subject: [PATCH 08/12] Revert "INJIWEB-1746 - Add logs for debug" (#965) * Revert "INJIWEB-1746 - All logs for debug" This reverts commit 6d6be79e31eba756add9f75262c7989bab824143. Signed-off-by: kongadurgesh * [INJIWEB-1746] - Fix logging mechanism Signed-off-by: kongadurgesh --------- Signed-off-by: kongadurgesh --- .../mosip/mimoto/service/impl/DataShareServiceImpl.java | 1 - .../mimoto/service/impl/PresentationServiceImpl.java | 8 +------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/java/io/mosip/mimoto/service/impl/DataShareServiceImpl.java b/src/main/java/io/mosip/mimoto/service/impl/DataShareServiceImpl.java index 936886333..7276819c1 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/DataShareServiceImpl.java +++ b/src/main/java/io/mosip/mimoto/service/impl/DataShareServiceImpl.java @@ -111,7 +111,6 @@ public VCCredentialResponse downloadCredentialFromDataShare(PresentationRequest } VCCredentialResponse vcCredentialResponse = objectMapper.readValue(vcCredentialResponseString, VCCredentialResponse.class); - log.info("Completed Mapping the Credential to Object => {}", vcCredentialResponse.toString()); if(vcCredentialResponse.getCredential() == null){ DataShareResponseDto dataShareResponse = objectMapper.readValue(vcCredentialResponseString, DataShareResponseDto.class); String errorCode = dataShareResponse.getErrors().get(0).getErrorCode(); diff --git a/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java b/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java index 89acc6d7b..71c77f6a7 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java +++ b/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java @@ -120,7 +120,6 @@ private List getPreRegisteredVerifiers() throws ApiNotAccessibleExcept @Override public String authorizePresentation(PresentationRequestDTO presentationRequestDTO) throws IOException { VCCredentialResponse vcCredentialResponse = dataShareService.downloadCredentialFromDataShare(presentationRequestDTO); - log.info("Downloaded credential from DataShare for presentation request, {}", vcCredentialResponse.toString()); PresentationDefinitionDTO presentationDefinitionDTO = presentationRequestDTO.getPresentationDefinition(); if (presentationDefinitionDTO == null) { throw new VPNotCreatedException(ErrorConstants.INVALID_REQUEST.getErrorMessage()); @@ -135,6 +134,7 @@ public String authorizePresentation(PresentationRequestDTO presentationRequestDT try { return processInputDescriptor(vcCredentialResponse, inputDescriptorDTO, presentationRequestDTO, presentationDefinitionDTO); } catch (JsonProcessingException e) { + log.error("Exception occured during processInputDesciptor: {}", e.getMessage()); throw new VPNotCreatedException(ErrorConstants.INVALID_REQUEST.getErrorMessage()); } }) @@ -146,32 +146,26 @@ private String processInputDescriptor(VCCredentialResponse vcCredentialResponse, String format = vcCredentialResponse.getFormat(); VerifiablePresentationDTO vpDTO; - log.info("Processing credential format: {}, CredentialFormat.LDP_VC.getFormat() : {}", format, CredentialFormat.LDP_VC.getFormat()); if (CredentialFormat.LDP_VC.getFormat().equalsIgnoreCase(format)) { VCCredentialProperties ldpCredential = objectMapper.convertValue(vcCredentialResponse.getCredential(), VCCredentialProperties.class); if (inputDescriptorDTO.getFormat().get("ldpVc").get("proofTypes") .stream().anyMatch(proofType -> ldpCredential.getProof().getType().equals(proofType))) { - log.info("Credential proof type matched: {}", ldpCredential.getProof().getType()); vpDTO = constructVerifiablePresentationString(ldpCredential); } else { - log.info("Credential proof type did not match. Expected one of: {}", inputDescriptorDTO.getFormat().get("ldpVc").get("proofTypes")); throw new VPNotCreatedException(ErrorConstants.INVALID_REQUEST.getErrorMessage()); } } else if (CredentialFormat.VC_SD_JWT.getFormat().equalsIgnoreCase(format) || CredentialFormat.DC_SD_JWT.getFormat().equalsIgnoreCase(format)) { - log.info("Processing SD-JWT credential"); String credential = objectMapper.convertValue(vcCredentialResponse.getCredential(), String.class); Map jwtHeaders = parseJwtHeader(credential); String responseAlgo = (String) jwtHeaders.get("alg"); if (inputDescriptorDTO.getFormat().get(format).get("sd-jwt_alg_values") .stream().anyMatch(responseAlgo::equals)) { - log.info("SD-JWT algorithm matched: {}", responseAlgo); vpDTO = constructVerifiablePresentationStringForSDjwt(credential); } else { throw new VPNotCreatedException(ErrorConstants.INVALID_REQUEST.getErrorMessage()); } } else { - log.info("Unsupported credential format: {}, CredentialFormat.LDP_VC.getFormat().equalsIgnoreCase(format): {}", format, CredentialFormat.LDP_VC.getFormat().equalsIgnoreCase(format)); throw new VPNotCreatedException(ErrorConstants.INVALID_REQUEST.getErrorMessage()); } From 9ae0fba8898014dec8dfbf5ff6ec89e89f22336a Mon Sep 17 00:00:00 2001 From: Durgesh Konga Date: Mon, 10 Nov 2025 14:17:40 +0530 Subject: [PATCH 09/12] Injiweb 1757 (#967) * [INJIWEB-1757] Add exclusions in pom.xml for junit dependency in kernel-core, remove direct junit dependency, remove unused h2database, remove logback dependency Signed-off-by: jackjain * [INJIWEB-1757] Remove unused variables from pom.xml Signed-off-by: jackjain --------- Signed-off-by: jackjain Co-authored-by: jackjain --- pom.xml | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/pom.xml b/pom.xml index 8447f75ba..f0cb7eed3 100644 --- a/pom.xml +++ b/pom.xml @@ -57,14 +57,9 @@ 3.4.0 3.4.1 - - 2.1.210 - 4.13.1 5.15.2 - - 1.2.3 2.13.2 @@ -117,6 +112,16 @@ io.mosip.kernel kernel-core 1.3.0-beta.1 + + + org.junit.vintage + junit-vintage-engine + + + junit + junit + + org.apache.logging.log4j @@ -223,10 +228,6 @@ spring-boot-starter-data-redis ${starter.redis.version} - - org.junit.vintage - junit-vintage-engine - @@ -284,27 +285,12 @@ ${junit.version} test - - com.h2database - h2 - ${h2.version} - org.slf4j slf4j-api 1.7.25 - - ch.qos.logback - logback-classic - 1.2.6 - - - ch.qos.logback - logback-core - 1.2.9 - joda-time joda-time From 5108369ccb49043164d989119689d7bbd4f4985e Mon Sep 17 00:00:00 2001 From: Durgesh Konga Date: Mon, 10 Nov 2025 17:43:18 +0530 Subject: [PATCH 10/12] [INJIWEB-1758] - Update Spring Boot version (#968) Signed-off-by: kongadurgesh --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f0cb7eed3..06d58d1ef 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ 3.6.3 - 3.3.7 + 3.3.10 3.4.0 3.4.1 From 2cc5b64887e334d0e6737628bd822679b162d31f Mon Sep 17 00:00:00 2001 From: Durgesh Konga Date: Tue, 11 Nov 2025 18:42:49 +0530 Subject: [PATCH 11/12] [INJIWEB-1757] - Remove unused dependencies (#970) * [INJIWEB-1757] - Remove unused dependencies Signed-off-by: kongadurgesh * [INJIWEB-1757] - Remove unused dependencies Signed-off-by: kongadurgesh --------- Signed-off-by: kongadurgesh --- pom.xml | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index 06d58d1ef..68f3deff8 100644 --- a/pom.xml +++ b/pom.xml @@ -136,16 +136,6 @@ identity-credential 20231002 - - info.weboftrust - ld-signatures-java - 1.5.0 - - - decentralized-identity - jsonld-common-java - 1.8.0 - io.mosip vcverifier-jar @@ -366,14 +356,6 @@ javax.activation activation - - javax.servlet - javax.servlet-api - - - org.glassfish.jaxb - jaxb-runtime - com.itextpdf itext7-core @@ -446,6 +428,12 @@ io.mosip.kernel kernel-biometrics-api ${kernel.biometrics.api.version} + + + org.junit.vintage + junit-vintage-engine + + @@ -457,6 +445,12 @@ io.mosip.kernel kernel-cbeffutil-api ${kernel.cbeffutil.version} + + + org.junit.vintage + junit-vintage-engine + + From 441908370a25beed5803064f87d19d360c5bb6f7 Mon Sep 17 00:00:00 2001 From: cyber-titan Date: Wed, 12 Nov 2025 10:22:46 +0530 Subject: [PATCH 12/12] injiweb-1671-credSub-optional added fix for missing keys in order Signed-off-by: cyber-titan --- .../impl/LdpVcCredentialFormatHandler.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java b/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java index 7aca5629b..43efba9f2 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java +++ b/src/main/java/io/mosip/mimoto/service/impl/LdpVcCredentialFormatHandler.java @@ -74,23 +74,25 @@ public LinkedHashMap> loadD String userLocale) { LinkedHashMap> displayProperties = new LinkedHashMap<>(); - List orderedKeys = credentialsSupportedResponse.getOrder(); + Set orderedKeys = Optional.ofNullable(credentialsSupportedResponse.getOrder()) + .map(LinkedHashSet::new) // preserve order + .orElse(new LinkedHashSet<>()); + + // Add remaining keys from credentialProperties that are not already in orderedKeys + for (String key : credentialProperties.keySet()) { + orderedKeys.add(key); + } // LDP VC format — display config is in "credential_definition.credential_subject" if (credentialsSupportedResponse.getCredentialDefinition() == null || credentialsSupportedResponse.getCredentialDefinition().getCredentialSubject() == null) { log.info("Issuer well-known has no credential definition or credential subject for LDP VC format; falling back to claim-based display properties"); - return buildFallbackDisplayProperties(credentialProperties, orderedKeys); + return buildFallbackDisplayProperties(credentialProperties, new ArrayList<>(orderedKeys)); } Map displayConfigMap = credentialsSupportedResponse.getCredentialDefinition().getCredentialSubject(); - if (displayConfigMap == null) { - log.info("No display configuration found in issuer well-known for LDP VC format; falling back to claim-based display properties"); - return buildFallbackDisplayProperties(credentialProperties, orderedKeys); - } - String resolvedLocale = LocaleUtils.resolveLocaleWithFallback(displayConfigMap, userLocale); LinkedHashMap localizedDisplayMap = new LinkedHashMap<>(); @@ -107,7 +109,7 @@ public LinkedHashMap> loadD addFallbackDisplayProperties(credentialProperties, localizedDisplayMap, resolvedLocale); List fieldKeys = (orderedKeys != null && !orderedKeys.isEmpty()) - ? orderedKeys + ? new ArrayList<>(orderedKeys) : new ArrayList<>(localizedDisplayMap.keySet()); for (String key : fieldKeys) {