diff --git a/pom.xml b/pom.xml index c79a35a..f979264 100644 --- a/pom.xml +++ b/pom.xml @@ -126,7 +126,7 @@ org.springframework spring-web - 6.1.6 + 6.1.12 diff --git a/src/main/java/tng/trustnetwork/keydistribution/repository/SignerInformationRepository.java b/src/main/java/tng/trustnetwork/keydistribution/repository/SignerInformationRepository.java index 78416ca..e34c0da 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/repository/SignerInformationRepository.java +++ b/src/main/java/tng/trustnetwork/keydistribution/repository/SignerInformationRepository.java @@ -33,6 +33,10 @@ public interface SignerInformationRepository extends JpaRepository getByDomainIsAndCountryIs(String domain, String country); + List findByCountryIn(List country); + + List findByGroupIn(List group); + List getByCountryIsAndGroupIs(String country, String group); List getByCountryIsAndGroupIsAndKidIs(String country, String group, String kid); diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/SignerInformationService.java b/src/main/java/tng/trustnetwork/keydistribution/service/SignerInformationService.java index bb5e478..7492def 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/service/SignerInformationService.java +++ b/src/main/java/tng/trustnetwork/keydistribution/service/SignerInformationService.java @@ -150,6 +150,15 @@ public List getCertificatesByCountry(String country) { return signerInformationRepository.getByCountryIs(country); } + public List getCertificatesByAllCountries(List countries) { + + return signerInformationRepository.findByCountryIn(countries); + } + + public List getCertificatesByAllGroups(List groups) { + + return signerInformationRepository.findByGroupIn(groups); + } /** * Returns signer information that are filtered by domain and participant. * @@ -157,6 +166,7 @@ public List getCertificatesByCountry(String country) { * @param country a participant aka country code, used as filter * @return matching SignerInformationEntities */ + public List getCertificatesByCountryDomain(String country, String domain) { return signerInformationRepository.getByDomainIsAndCountryIs(domain, country); diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/did/DidTrustListService.java b/src/main/java/tng/trustnetwork/keydistribution/service/did/DidTrustListService.java index 10866cf..1c3447a 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/service/did/DidTrustListService.java +++ b/src/main/java/tng/trustnetwork/keydistribution/service/did/DidTrustListService.java @@ -49,6 +49,8 @@ import java.util.Map; import java.util.MissingResourceException; import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -111,6 +113,8 @@ private class DidSpecification { private final Supplier> issuerSupplier; + private final Supplier> didPathSupplier; + public List getPath(boolean ref) { ArrayList path = new ArrayList<>(this.path); path.add(0, getListPathElement(ref)); @@ -163,49 +167,91 @@ public void job() { didSpecifications.add(new DidSpecification( Collections.emptyList(), signerInformationService::getAllCertificates, - trustedIssuerService::getAllDid)); + trustedIssuerService::getAllDid, + () -> Stream.concat(Stream.of(WILDCARD_CHAR), domains.stream()).collect(Collectors.toList()) + )); // Add all Domain DID - domains.forEach( - domain -> didSpecifications.add(new DidSpecification( + domains.forEach(domain -> { + List signerInformationEntitiesList = + signerInformationService.getCertificatesByDomain(domain); + didSpecifications.add(new DidSpecification( List.of(domain), - () -> signerInformationService.getCertificatesByDomain(domain), - () -> trustedIssuerService.getAllDid(domain, null)))); + () -> signerInformationEntitiesList, + () -> trustedIssuerService.getAllDid(domain, null), + () -> new ArrayList<>( + Stream.concat(Stream.of(WILDCARD_CHAR),signerInformationEntitiesList + .stream().map(entity -> getParticipantCode(entity.getCountry()))).collect(Collectors.toSet())) + )); + }); // Add all Country and Domain specific DID domains.forEach( - domain -> countries.forEach( - country -> didSpecifications.add(new DidSpecification( + domain -> countries.forEach(country -> { + List signerInformationEntitiesList = + signerInformationService.getCertificatesByCountryDomain(country, domain); + didSpecifications.add(new DidSpecification( List.of(domain, getParticipantCode(country)), - () -> signerInformationService.getCertificatesByCountryDomain(country, domain), - () -> trustedIssuerService.getAllDid(domain, country)) - ))); + () -> signerInformationEntitiesList, + () -> trustedIssuerService.getAllDid(domain, country), + () -> new ArrayList<>( + signerInformationEntitiesList.stream() + .filter(signerInformationEntity -> isDeniedGroup(signerInformationEntity.getGroup())) + .map(entity -> getMappedGroupName(entity.getGroup())).collect(Collectors.toSet())))); + })); + + // Add all Country and Domain independant DID + didSpecifications.add(new DidSpecification( + List.of(WILDCARD_CHAR), + () -> signerInformationService.getCertificatesByAllCountries(countries), + Collections::emptyList, + () -> new ArrayList<>( + Stream.concat(Stream.of(WILDCARD_CHAR), countries.stream().map(country -> getParticipantCode(country))) + .collect(Collectors.toSet())))); + + // Add all Domain independent and country specific DID countries.forEach( - country -> didSpecifications.add(new DidSpecification( - List.of(WILDCARD_CHAR, getParticipantCode(country)), - () -> signerInformationService.getCertificatesByCountry(country), - () -> trustedIssuerService.getAllDid(null, country)))); + country -> { + List signerInformationEntitiesList = + signerInformationService.getCertificatesByCountry(country); + didSpecifications.add(new DidSpecification( + List.of(WILDCARD_CHAR, getParticipantCode(country)), + () -> signerInformationEntitiesList, + () -> trustedIssuerService.getAllDid(null, country), + () -> new ArrayList<>( + signerInformationEntitiesList.stream() + .filter(signerInformation -> isDeniedGroup(signerInformation.getGroup())) + .map(entity -> getMappedGroupName(entity.getGroup())).collect(Collectors.toSet())) + )); + }); // Add all domain, country and group specific did domains.forEach( domain -> countries.forEach( country -> groups.forEach( - group -> didSpecifications.add(new DidSpecification( - List.of(domain, getParticipantCode(country), getMappedGroupName(group)), - () -> signerInformationService.getCertificatesByDomainParticipantGroup(domain, country, group), - Collections::emptyList))))); + group -> { + List signerInformationEntitiesList = + signerInformationService.getCertificatesByDomainParticipantGroup(domain, country, group); + didSpecifications.add(new DidSpecification( + List.of(domain, getParticipantCode(country), getMappedGroupName(group)), + () -> signerInformationEntitiesList, + Collections::emptyList, + () -> new ArrayList<>( + signerInformationEntitiesList.stream().map(entity -> encodeKid(entity.getKid())) + .collect(Collectors.toSet())))); + }))); // Add all domain, country, group, kid specific did domains.forEach( domain -> countries.forEach( country -> groups.forEach( group -> { - List entityList = + List signerInformationEntitiesList = signerInformationService.getCertificatesByDomainParticipantGroup(domain, country, group); - entityList.forEach(entity -> { + signerInformationEntitiesList.forEach(entity -> { didSpecifications.add(new DidSpecification( List.of(domain, getParticipantCode(country), getMappedGroupName(group), encodeKid(entity.getKid())), @@ -213,6 +259,7 @@ public void job() { () -> signerInformationService.getCertificatesByDomainParticipantGroupKid( domain, country, group, entity.getKid()), + Collections::emptyList, Collections::emptyList )); }); @@ -223,19 +270,26 @@ public void job() { // Add all country and group specific did countries.forEach( country -> groups.forEach( - group -> didSpecifications.add(new DidSpecification( - List.of(WILDCARD_CHAR, getParticipantCode(country), getMappedGroupName(group)), - () -> signerInformationService.getCertificatesByGroupCountry(group, country), - Collections::emptyList)))); + group -> { + List signerInformationEntitiesList = + signerInformationService.getCertificatesByGroupCountry(group, country); + didSpecifications.add(new DidSpecification( + List.of(WILDCARD_CHAR, getParticipantCode(country), getMappedGroupName(group)), + () -> signerInformationEntitiesList, + Collections::emptyList, + () -> new ArrayList<>( + signerInformationEntitiesList.stream().map(entity -> encodeKid(entity.getKid())) + .collect(Collectors.toSet())))); + })); // Add all country, group, kid specific did countries.forEach( country -> groups.forEach( group -> { - List entityList = + List signerInformationEntitiesList = signerInformationService.getCertificatesByGroupCountry(group, country); - entityList.forEach(entity -> { + signerInformationEntitiesList.forEach(entity -> { didSpecifications.add(new DidSpecification( List.of(WILDCARD_CHAR, getParticipantCode(country), getMappedGroupName(group), @@ -244,6 +298,7 @@ public void job() { () -> signerInformationService.getCertificatesByKidGroupCountry( country, group, entity.getKid()), + Collections::emptyList, Collections::emptyList )); }); @@ -252,18 +307,45 @@ public void job() { // Add all domain and group specific did domains.forEach( domain -> groups.forEach( - group -> didSpecifications.add(new DidSpecification( - List.of(domain, WILDCARD_CHAR, getMappedGroupName(group)), - () -> signerInformationService.getCertificatesByDomainGroup(domain, group), - Collections::emptyList)))); + group -> { + List signerInformationEntitiesList = + signerInformationService.getCertificatesByDomainGroup(domain, group); + didSpecifications.add(new DidSpecification( + List.of(domain, WILDCARD_CHAR, getMappedGroupName(group)), + () -> signerInformationEntitiesList, + Collections::emptyList, + () -> new ArrayList<>( + signerInformationEntitiesList.stream().map(entity -> encodeKid(entity.getKid())) + .collect(Collectors.toSet())))); + + })); + + // Add all country independent but domain and group specific did + domains.forEach( + domain -> { + List signerInformationEntitiesList = + signerInformationService.getCertificatesByDomain(domain); + didSpecifications.add(new DidSpecification( + List.of(domain, WILDCARD_CHAR), + () -> signerInformationEntitiesList, + Collections::emptyList, + () -> new ArrayList<>( + signerInformationEntitiesList.stream() + .filter(domainEntity -> isDeniedGroup(domainEntity.getGroup())) + .map(domainEntity -> getMappedGroupName(domainEntity.getGroup())) + .collect(Collectors.toSet())) + )); + }); + + // Add all domain, group and kid specific did domains.forEach( domain -> groups.forEach( group -> { - List entityList = + List signerInformationEntitiesList = signerInformationService.getCertificatesByDomainGroup(domain, group); - entityList.forEach(entity -> { + signerInformationEntitiesList.forEach(entity -> { didSpecifications.add(new DidSpecification( List.of(domain, WILDCARD_CHAR, getMappedGroupName(group), encodeKid(entity.getKid())), @@ -271,29 +353,52 @@ public void job() { () -> signerInformationService.getCertificatesByDomainGroupKid( domain, group, entity.getKid()), + Collections::emptyList, Collections::emptyList )); }); })); + + // Add all group specific did groups.forEach( - group -> didSpecifications.add(new DidSpecification( - List.of(WILDCARD_CHAR, WILDCARD_CHAR, getMappedGroupName(group)), - () -> signerInformationService.getCertificatesByGroup(group), - Collections::emptyList))); + group -> { + List signerInformationEntitiesList = + signerInformationService.getCertificatesByGroup(group); + didSpecifications.add(new DidSpecification( + List.of(WILDCARD_CHAR, WILDCARD_CHAR, getMappedGroupName(group)), + () -> signerInformationEntitiesList, + Collections::emptyList, + () -> new ArrayList<>( + signerInformationEntitiesList.stream() + .map(entity -> encodeKid(entity.getKid())) + .collect(Collectors.toSet())))); + }); + + // Add all domain country independent and group specific did + didSpecifications.add(new DidSpecification( + List.of(WILDCARD_CHAR, WILDCARD_CHAR), + () -> signerInformationService.getCertificatesByAllGroups(groups), + Collections::emptyList, + () -> new ArrayList<>( + groups.stream().filter(group -> isDeniedGroup(group)) + .map(group -> getMappedGroupName(group)) + .collect(Collectors.toSet())))); // Add all group, kid specific did groups.forEach( group -> { - List entityList = signerInformationService.getCertificatesByGroup(group); - entityList.forEach(entity -> { + List signerInformationEntitiesList = + signerInformationService.getCertificatesByGroup(group); + signerInformationEntitiesList.forEach(entity -> { didSpecifications.add(new DidSpecification( List.of(WILDCARD_CHAR, WILDCARD_CHAR, getMappedGroupName(group), encodeKid(entity.getKid())), () -> signerInformationService.getCertificatesByGroupKid(group, entity.getKid()), + Collections::emptyList, Collections::emptyList )); }); @@ -335,6 +440,7 @@ private String generateTrustList(DidSpecification specification, boolean onlyRef List signerInformationEntities = filterEntities(specification.getCertSupplier().get()); List trustedIssuerEntities = specification.getIssuerSupplier().get(); + List didRefPathList = specification.getDidPathSupplier().get(); if (signerInformationEntities.isEmpty() && trustedIssuerEntities.isEmpty()) { log.info("Empty DID for path {}", specification.getPath()); @@ -348,15 +454,28 @@ private String generateTrustList(DidSpecification specification, boolean onlyRef trustList.setVerificationMethod(new ArrayList<>()); // Add Certificates + if (onlyReferences) { + if (didRefPathList.isEmpty()) { + trustList.getVerificationMethod().add(specification.getDocumentId(false)); + } else { + didRefPathList.forEach(path -> { + trustList.getVerificationMethod() + .add(specification.getDocumentId(true) + SEPARATOR_DID_PATH + path); + }); + } + + trustedIssuerEntities.forEach(did -> { + if (!trustList.getVerificationMethod().contains(did.getUrl())) { + trustList.getVerificationMethod().add(did.getUrl()); + } + }); - for (SignerInformationEntity signerInformationEntity : signerInformationEntities) { + } else { + for (SignerInformationEntity signerInformationEntity : signerInformationEntities) { - if (onlyReferences) { - trustList.getVerificationMethod().add( - specification.getEntryId(encodeKid(signerInformationEntity.getKid()))); + X509Certificate parsedCertificate = + kdsCertUtils.parseCertificate(signerInformationEntity.getRawData()); - } else { - X509Certificate parsedCertificate = kdsCertUtils.parseCertificate(signerInformationEntity.getRawData()); if (parsedCertificate == null) { log.error("Could not parse cert {} of country {}", signerInformationEntity.getKid(), @@ -384,11 +503,6 @@ private String generateTrustList(DidSpecification specification, boolean onlyRef } } - // Add Trusted Issuer (DID References) - // TODO: Add filtering for TrustedIssuers - if (onlyReferences) { - trustedIssuerEntities.forEach(did -> trustList.getVerificationMethod().add(did.getUrl())); - } // Sign Document JsonWebSignature2020LdSigner signer = new JsonWebSignature2020LdSigner(byteSigner); @@ -453,8 +567,7 @@ private void addTrustListEntry(DidTrustList trustList, private List filterEntities(List entities) { return entities.stream() - .filter(entity -> kdsConfigProperties.getDid().getGroupDenyList().stream() - .noneMatch(e -> entity.getGroup().equalsIgnoreCase(e))) + .filter(entity -> isDeniedGroup(entity.getGroup())) .toList(); } @@ -464,6 +577,12 @@ private String getMappedGroupName(String groupName) { .computeIfAbsent(groupName, g -> g); } + + private boolean isDeniedGroup(String group) { + return kdsConfigProperties.getDid().getGroupDenyList().stream() + .noneMatch(e -> group.equalsIgnoreCase(e)); + } + /** * Recursively resolve certificate chains based on current database. * Resolving is done country-code and domain aware. diff --git a/src/test/java/tng/trustnetwork/keydistribution/service/DidTrustListServiceTest.java b/src/test/java/tng/trustnetwork/keydistribution/service/DidTrustListServiceTest.java index 91b1bc5..e4181ff 100644 --- a/src/test/java/tng/trustnetwork/keydistribution/service/DidTrustListServiceTest.java +++ b/src/test/java/tng/trustnetwork/keydistribution/service/DidTrustListServiceTest.java @@ -206,7 +206,7 @@ void testTrustList(boolean isEcAlgorithm) throws Exception { didTrustListService.job(); - Assertions.assertEquals(104, uploadArgumentCaptor.getAllValues().size()); + Assertions.assertEquals(110, uploadArgumentCaptor.getAllValues().size()); int expectedNullDid = 32; @@ -239,7 +239,41 @@ void testTrustList(boolean isEcAlgorithm) throws Exception { assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist#" + encodeKid(certCscaEuKid)), certCscaEuKid, certCscaEu, null, "did:web:abc:trustlist"); break; - + case "did:web:abc:trustlist:-": + Assertions.assertEquals("did:web:abc:trustlist:-", parsed.getController()); + Assertions.assertEquals(4, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-#" + encodeKid(certDscDeKid)), + certDscDeKid, certDscDe, certCscaDe, "did:web:abc:trustlist:-"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-#" + encodeKid(certCscaDeKid)), + certCscaDeKid, certCscaDe, null, "did:web:abc:trustlist:-"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-#" + encodeKid(certDscEuKid)), + certDscEuKid, certDscEu, certCscaEu, "did:web:abc:trustlist:-"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-#" + encodeKid(certCscaEuKid)), + certCscaEuKid, certCscaEu, null, "did:web:abc:trustlist:-"); + break; + + case "did:web:abc:trustlist:-:-": + Assertions.assertEquals("did:web:abc:trustlist:-:-", parsed.getController()); + Assertions.assertEquals(4, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-:-#" + encodeKid(certDscDeKid)), + certDscDeKid, certDscDe, certCscaDe, "did:web:abc:trustlist:-:-"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-:-#" + encodeKid(certDscEuKid)), + certDscEuKid, certDscEu, certCscaEu, "did:web:abc:trustlist:-:-"); + break; + + case "did:web:abc:trustlist:DCC:-": + Assertions.assertEquals("did:web:abc:trustlist:DCC:-", parsed.getController()); + Assertions.assertEquals(4, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC:-#" + encodeKid(certDscDeKid)), + certDscDeKid, certDscDe, certCscaDe, "did:web:abc:trustlist:DCC:-"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC:-#" + encodeKid(certDscEuKid)), + certDscEuKid, certDscEu, certCscaEu, "did:web:abc:trustlist:DCC:-"); + break; + + case "did:web:abc:trustlist:DCC:XEU:DSC": Assertions.assertEquals("did:web:abc:trustlist:DCC:XEU:DSC", parsed.getController()); Assertions.assertEquals(1, parsed.getVerificationMethod().size());