diff --git a/README.md b/README.md index 058a771..7d81b43 100644 --- a/README.md +++ b/README.md @@ -1,74 +1,76 @@ -

- EU Digital COVID Certificate Validation Decorator -

- -

- - - - - - - - - - - - -

- -

- About • - Development • - Documentation • - Support • - Contribute • - Licensing -

- -## About - -This repository contains the source code of the EU Digital COVID Certificate Validation Decorator. - -- [ ] TODO: Describe Component - -## Development - -### Prerequisites - -- [ ] TODO: Prerequisites - -### Build - -- [ ] TODO: Build - -## Documentation - -- [ ] TODO: Documentation - -## Support and feedback - -The following channels are available for discussions, feedback, and support requests: - -| Type | Channel | -| ------------------------ | ------------------------------------------------------ | -| **Issues** | | -| **Other requests** | | - -## How to contribute - -Contribution and feedback is encouraged and always welcome. For more information about how to contribute, the project structure, -as well as additional contribution information, see our [Contribution Guidelines](./CONTRIBUTING.md). By participating in this -project, you agree to abide by its [Code of Conduct](./CODE_OF_CONDUCT.md) at all times. - -## Licensing - -Copyright (C) 2021 T-Systems International GmbH and all other contributors - -Licensed under the **Apache License, Version 2.0** (the "License"); you may not use this file except in compliance with the License. - -You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0. - -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" -BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the [LICENSE](./LICENSE) for the specific -language governing permissions and limitations under the License. +

+ EU Digital COVID Certificate Validation Decorator +

+ +

+ + + + + + + + + + + + +

+ +

+ About • + Development • + Documentation • + Support • + Contribute • + Licensing +

+ +## About + +This repository contains the source code of the EU Digital COVID Certificate Validation Decorator. + +The Validation Decorator is a template. To make your own adjustments, the three interfaces `KeyProvider`, `BackendRepository` and `AccessTokenPayloadBuilder` should be implemented. + +- [ ] TODO: Describe Component + +## Development + +### Prerequisites + +- [ ] TODO: Prerequisites + +### Build + +- [ ] TODO: Build + +## Documentation + +- [ ] TODO: Documentation + +## Support and feedback + +The following channels are available for discussions, feedback, and support requests: + +| Type | Channel | +| ------------------------ | ------------------------------------------------------ | +| **Issues** | | +| **Other requests** | | + +## How to contribute + +Contribution and feedback is encouraged and always welcome. For more information about how to contribute, the project structure, +as well as additional contribution information, see our [Contribution Guidelines](./CONTRIBUTING.md). By participating in this +project, you agree to abide by its [Code of Conduct](./CODE_OF_CONDUCT.md) at all times. + +## Licensing + +Copyright (C) 2021 T-Systems International GmbH and all other contributors + +Licensed under the **Apache License, Version 2.0** (the "License"); you may not use this file except in compliance with the License. + +You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" +BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the [LICENSE](./LICENSE) for the specific +language governing permissions and limitations under the License. diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/AccessTokenPayloadBuilder.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/AccessTokenPayloadBuilder.java new file mode 100644 index 0000000..6aa4f5c --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/AccessTokenPayloadBuilder.java @@ -0,0 +1,35 @@ +/*- + * ---license-start + * European Digital COVID Certificate Validation Decorator Service / dgca-validation-decorator + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.validation.decorator.service; + +import eu.europa.ec.dgc.validation.decorator.dto.AccessTokenPayload; +import eu.europa.ec.dgc.validation.decorator.entity.ServiceTokenContentResponse.OccurrenceInfoResponse; +import eu.europa.ec.dgc.validation.decorator.entity.ServiceTokenContentResponse.SubjectResponse; +import eu.europa.ec.dgc.validation.decorator.entity.ValidationServiceInitializeResponse; + +public interface AccessTokenPayloadBuilder { + + AccessTokenPayload build( + final String subject, + final ValidationServiceInitializeResponse initialize, + final SubjectResponse subjectResponse, + final OccurrenceInfoResponse occurrenceInfo); +} \ No newline at end of file diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/AccessTokenPayloadBuilderImpl.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/AccessTokenPayloadBuilderImpl.java new file mode 100644 index 0000000..322754c --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/AccessTokenPayloadBuilderImpl.java @@ -0,0 +1,102 @@ +/*- + * ---license-start + * European Digital COVID Certificate Validation Decorator Service / dgca-validation-decorator + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package eu.europa.ec.dgc.validation.decorator.service; + +import eu.europa.ec.dgc.validation.decorator.config.DgcProperties; +import eu.europa.ec.dgc.validation.decorator.dto.AccessTokenPayload; +import eu.europa.ec.dgc.validation.decorator.dto.AccessTokenPayload.AccessTokenConditions; +import eu.europa.ec.dgc.validation.decorator.entity.ServiceTokenContentResponse.OccurrenceInfoResponse; +import eu.europa.ec.dgc.validation.decorator.entity.ServiceTokenContentResponse.SubjectResponse; +import eu.europa.ec.dgc.validation.decorator.entity.ValidationServiceInitializeResponse; +import java.time.Instant; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class AccessTokenPayloadBuilderImpl implements AccessTokenPayloadBuilder { + + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxxxx"); + + private static final DateTimeFormatter DOB_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + private final DgcProperties dgcProperties; + + @Override + public AccessTokenPayload build( + final String subject, + final ValidationServiceInitializeResponse initialize, + final SubjectResponse subjectResponse, + final OccurrenceInfoResponse occurrenceInfo) { + final AccessTokenConditions accessTokenConditions = new AccessTokenConditions(); + accessTokenConditions.setLang(occurrenceInfo.getLanguage()); + accessTokenConditions.setFnt(subjectResponse.getForename()); + accessTokenConditions.setGnt(subjectResponse.getLastname()); + accessTokenConditions.setCoa(occurrenceInfo.getCountryOfArrival()); + accessTokenConditions.setCod(occurrenceInfo.getCountryOfDeparture()); + accessTokenConditions.setRoa(occurrenceInfo.getRegionOfArrival()); + accessTokenConditions.setRod(occurrenceInfo.getRegionOfDeparture()); + accessTokenConditions.setType(occurrenceInfo.getConditionTypes()); + accessTokenConditions.setCategory(occurrenceInfo.getCategories()); + accessTokenConditions.setDob(this.parseBirthDay(subjectResponse.getBirthDate())); + + final OffsetDateTime departureTime = occurrenceInfo.getDepartureTime(); + accessTokenConditions.setValidFrom(departureTime.format(FORMATTER)); + accessTokenConditions.setValidationClock(occurrenceInfo.getArrivalTime().format(FORMATTER)); + accessTokenConditions.setValidTo(departureTime.plusDays(2).format(FORMATTER)); + + final AccessTokenPayload accessTokenPayload = new AccessTokenPayload(); + accessTokenPayload.setJti(subjectResponse.getJti()); + accessTokenPayload.setIss(this.dgcProperties.getToken().getIssuer()); + accessTokenPayload.setIat(Instant.now().getEpochSecond()); + accessTokenPayload.setExp(initialize.getExp()); + accessTokenPayload.setSub(subject); + accessTokenPayload.setAud(initialize.getAud()); + accessTokenPayload.setType(occurrenceInfo.getType()); + accessTokenPayload.setConditions(accessTokenConditions); + accessTokenPayload.setVersion("1.0"); + return accessTokenPayload; + } + + private String parseBirthDay(final String in) { + if (in != null && !in.isBlank()) { + try { + return OffsetDateTime.parse(in).format(DOB_FORMATTER); + } catch (Exception e) { + // not handle + } + try { + return LocalDate.parse(in, DateTimeFormatter.ofPattern("MM-dd-yyyy")).format(DOB_FORMATTER); + } catch (Exception e) { + // not handle + } + try { + return in.substring(0, 10); + } catch (Exception e) { + // not handle + } + } + return in; + } +} diff --git a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/DccTokenService.java b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/DccTokenService.java index 4c71d3d..8838524 100644 --- a/src/main/java/eu/europa/ec/dgc/validation/decorator/service/DccTokenService.java +++ b/src/main/java/eu/europa/ec/dgc/validation/decorator/service/DccTokenService.java @@ -20,10 +20,8 @@ package eu.europa.ec.dgc.validation.decorator.service; -import eu.europa.ec.dgc.validation.decorator.config.DgcProperties; import eu.europa.ec.dgc.validation.decorator.config.DgcProperties.ServiceProperties; import eu.europa.ec.dgc.validation.decorator.dto.AccessTokenPayload; -import eu.europa.ec.dgc.validation.decorator.dto.AccessTokenPayload.AccessTokenConditions; import eu.europa.ec.dgc.validation.decorator.dto.DccTokenRequest; import eu.europa.ec.dgc.validation.decorator.entity.ServiceTokenContentResponse; import eu.europa.ec.dgc.validation.decorator.entity.ServiceTokenContentResponse.OccurrenceInfoResponse; @@ -35,9 +33,6 @@ import eu.europa.ec.dgc.validation.decorator.repository.BackendRepository; import eu.europa.ec.dgc.validation.decorator.repository.ValidationServiceRepository; import java.security.SecureRandom; -import java.time.Instant; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; import java.util.Base64; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -51,16 +46,14 @@ public class DccTokenService { private static final String TYPE_VALIDATION_SERVICE = "ValidationService"; - private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxxxx"); - - private final DgcProperties dgcProperties; - private final ValidationServiceRepository validationServiceRepository; private final BackendRepository backendRepository; private final IdentityService identityService; + private final AccessTokenPayloadBuilder accessTokenPayloadBuilder; + /** * Request validation- and backend service to create token. * @@ -85,7 +78,7 @@ public AccessTokenPayload getAccessTockenForValidationService( final SubjectResponse subjectResponse = tokenContent.getSubjects().get(0); final OccurrenceInfoResponse occurrenceInfo = tokenContent.getOccurrenceInfo(); - final AccessTokenPayload accessToken = this.buildAccessToken( + final AccessTokenPayload accessToken = this.accessTokenPayloadBuilder.build( subject, initialize, subjectResponse, occurrenceInfo); accessToken.setNonce(nonce); return accessToken; @@ -97,43 +90,6 @@ private String buildNonce() { return Base64.getEncoder().encodeToString(randomBytes); } - private AccessTokenPayload buildAccessToken( - final String subject, - final ValidationServiceInitializeResponse initialize, - final SubjectResponse subjectResponse, - final OccurrenceInfoResponse occurrenceInfo) { - final AccessTokenConditions accessTokenConditions = new AccessTokenConditions(); - accessTokenConditions.setLang(occurrenceInfo.getLanguage()); - accessTokenConditions.setFnt(subjectResponse.getForename()); - accessTokenConditions.setGnt(subjectResponse.getLastname()); - accessTokenConditions.setCoa(occurrenceInfo.getCountryOfArrival()); - accessTokenConditions.setCod(occurrenceInfo.getCountryOfDeparture()); - accessTokenConditions.setRoa(occurrenceInfo.getRegionOfArrival()); - accessTokenConditions.setRod(occurrenceInfo.getRegionOfDeparture()); - accessTokenConditions.setType(occurrenceInfo.getConditionTypes()); - accessTokenConditions.setCategory(occurrenceInfo.getCategories()); - if (subjectResponse.getBirthDate() != null && !subjectResponse.getBirthDate().isBlank()) { - accessTokenConditions.setDob(subjectResponse.getBirthDate()); - } - - final OffsetDateTime departureTime = occurrenceInfo.getDepartureTime(); - accessTokenConditions.setValidFrom(departureTime.format(FORMATTER)); - accessTokenConditions.setValidationClock(occurrenceInfo.getArrivalTime().format(FORMATTER)); - accessTokenConditions.setValidTo(departureTime.plusDays(2).format(FORMATTER)); - - final AccessTokenPayload accessTokenPayload = new AccessTokenPayload(); - accessTokenPayload.setJti(subjectResponse.getJti()); - accessTokenPayload.setIss(this.dgcProperties.getToken().getIssuer()); - accessTokenPayload.setIat(Instant.now().getEpochSecond()); - accessTokenPayload.setExp(initialize.getExp()); - accessTokenPayload.setSub(subject); - accessTokenPayload.setAud(initialize.getAud()); - accessTokenPayload.setType(occurrenceInfo.getType()); - accessTokenPayload.setConditions(accessTokenConditions); - accessTokenPayload.setVersion("1.0"); - return accessTokenPayload; - } - private ValidationServiceInitializeResponse getValidationServiceInitialize(final DccTokenRequest dccToken, final String subject, final ServiceProperties service, final String nonce) { try {