Skip to content

Commit

Permalink
[INJICERT-434] improve JSON templating edge cases (#112)
Browse files Browse the repository at this point in the history
  • Loading branch information
vharsh authored Oct 20, 2024
1 parent e87f210 commit 1ddbabb
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

import io.mosip.certify.api.spi.VCFormatter;
import io.mosip.certify.core.constants.Constants;
import io.mosip.certify.core.constants.VCDM2Constants;
import io.mosip.certify.core.repository.TemplateRepository;
import jakarta.annotation.PostConstruct;
import lombok.SneakyThrows;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.tools.generic.DateTool;
import org.apache.velocity.tools.generic.EscapeTool;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -58,6 +62,7 @@ public void initialize() {
* internal work such as locating the appropriate template
* @return templated VC as a String
*/
@SneakyThrows
@Override
public String format(Map<String, Object> templateInput, Map<String, Object> defaultSettings) {
// TODO: Isn't template name becoming too complex with VC_CONTEXTS & CREDENTIAL_TYPES both?
Expand All @@ -66,6 +71,7 @@ public String format(Map<String, Object> templateInput, Map<String, Object> defa
String t = templateCache.get(templateName);
StringWriter writer = new StringWriter();
// 1. Prepare map
// TODO: Eventually, the credentialSubject from the plugin will be templated as-is
Map<String, Object> finalTemplate = new HashMap<>();
for (String key : templateInput.keySet()) {
Object value = templateInput.get(key);
Expand All @@ -76,15 +82,22 @@ public String format(Map<String, Object> templateInput, Map<String, Object> defa
finalTemplate.put(key, new JSONArray((List<Object>) value));
} else if (value.getClass().isArray()) {
finalTemplate.put(key, new JSONArray(List.of(value)));
} else {
} else if (value instanceof Integer | value instanceof Float | value instanceof Long | value instanceof Double) {
// entities which don't need to be quoted
finalTemplate.put(key, value);
} else if (value instanceof String){
// entities which need to be quoted
finalTemplate.put(key, JSONObject.wrap(value));
}
}
// Date: https://velocity.apache.org/tools/3.1/apidocs/org/apache/velocity/tools/generic/DateTool.html
finalTemplate.put("_dateTool", new DateTool());
// Escape: https://velocity.apache.org/tools/3.1/apidocs/org/apache/velocity/tools/generic/EscapeTool.html
finalTemplate.put("_esc", new EscapeTool());
// add the issuer value
finalTemplate.put("issuer", issuer);
if (shouldHaveDates && !(templateInput.containsKey(VCDM2Constants.VALID_FROM)
&& templateInput.containsKey(VCDM2Constants.VALID_UNITL))) {
templateInput.put("_dateTool", new DateTool());
String time = ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN));
// hardcoded time
String expiryTime = ZonedDateTime.now(ZoneOffset.UTC).plusYears(2).format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,4 +329,4 @@ spring.datasource.password=postgres
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@
import junit.framework.TestCase;
import lombok.SneakyThrows;
import net.javacrumbs.jsonunit.assertj.JsonAssertions;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand All @@ -32,11 +34,11 @@ public class VelocityTemplatingEngineImplTest extends TestCase {
@Mock
TemplateRepository templateRepository;

@SneakyThrows
@Before
public void setUp() {
List<TemplateData> templates = new ArrayList<>();
TemplateData vc1 = initTemplate("""
{
"@context": [
"https://www.w3.org/2018/credentials/v1"]
Expand Down Expand Up @@ -87,9 +89,37 @@ public void setUp() {
""",
"MockVerifiableCredential,VerifiableCredential",
"https://example.org/Person.json,https://www.w3.org/ns/credentials/v2"

);
when(templateRepository.findAll()).thenReturn(List.of(vc1, vc2));
TemplateData vc3 = initTemplate("""
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://vharsh.github.io/DID/mock-context.json"],
"issuer": "${issuer}",
"type": ["VerifiableCredential", "MockVerifiableCredential"],
"issuanceDate": "${validFrom}",
"expirationDate": "${validUntil}",
"credentialSubject": {
"gender": ${gender},
"postalCode": ${postalCode},
"fullName": ${fullName},
"dateOfBirth": "${dateOfBirth}",
"province": ${province},
"phone": "${phone}",
"addressLine1": ${addressLine1},
"region": ${region},
"vcVer": "${vcVer}",
"UIN": ${UIN},
"email": "${email}",
"face": "${face}"
}
}
""",
"MockVerifiableCredential,VerifiableCredential",
"https://vharsh.github.io/DID/mock-context.json,https://www.w3.org/2018/credentials/v1"
);
when(templateRepository.findAll()).thenReturn(List.of(vc1, vc2, vc3));
ReflectionTestUtils.setField(formatter, "shouldHaveDates", true);
formatter.initialize();
// engine = new VelocityEngine();
// engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
Expand All @@ -111,11 +141,6 @@ private TemplateData initTemplate(String template, String type, String context)
@SneakyThrows
@Test
public void testTemplating() {
// 1. setup template
Resource cr = new ClassPathResource("MockCredential1.vm");
assert cr.isFile();
String t = Files.readString(cr.getFile().toPath(), StandardCharsets.UTF_8);
assert t != null;
Map<String, Object> ret = new HashMap<>();
ret.put("vcVer", "VC-V1");
// ret.put("issuer", "https://example.com/fake-issuer");
Expand All @@ -142,4 +167,37 @@ public void testTemplating() {
""";
JsonAssertions.assertThatJson(actualJSON).isEqualTo(expectedJSON);
}

@Ignore
@Test
public void testTemplating_localOnly() {
// This test is written to rapidly test out changes against a hosted mock-identity system
RestTemplate r = new RestTemplate();
Map<String, Object> x = r.getForObject("http://localhost:8082/v1/mock-identity-system/identity/12345678",
HashMap.class);
Map<String, Object> res = (Map<String, Object>) x.get("response");
Map<String, Object> ret = new HashMap<>();
ret.put("vcVer", "VC-V1");
ret.put("UIN", 1234567);
ret.put("fullName", res.get("fullName"));
ret.put("gender", res.get("gender"));
ret.put("dateOfBirth", res.get("dateOfBirth"));
ret.put("email", res.get("email"));
ret.put("phone", res.get("phone"));
ret.put("addressLine1", res.get("streetAddress"));
ret.put("province", res.get("locality"));
ret.put("region", res.get("region"));
ret.put("postalCode", res.get("postalCode"));
ret.put("face", res.get("encodedPhoto"));

Map<String, Object> templateMap = Map.of("templateName", "MockVerifiableCredential,VerifiableCredential:https://vharsh.github.io/DID/mock-context.json,https://www.w3.org/2018/credentials/v1",
"issuerURI", "https://example.com/fake-issuer");
String actualJSON = formatter.format(ret, templateMap);
System.out.println(actualJSON);
try {
JSONObject j = new JSONObject(actualJSON);
} catch (JSONException e) {
Assert.fail(e.getMessage());
}
}
}

0 comments on commit 1ddbabb

Please sign in to comment.