Skip to content
This repository has been archived by the owner on Sep 15, 2023. It is now read-only.

Commit

Permalink
Merge pull request #23 from admin-ch/feature/sdk-3.0
Browse files Browse the repository at this point in the history
Update SDK to 3.0.0 with verification mode support
  • Loading branch information
gstoehld authored Dec 14, 2021
2 parents 804c9f7 + 3af13f6 commit 333c5ce
Show file tree
Hide file tree
Showing 14 changed files with 375 additions and 140 deletions.
240 changes: 139 additions & 101 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,20 @@ The service is now exposed on port 8080 (change the first number in the `-p` arg

Certificates can be verified by sending POST requests to `/v1/verify`, where the payload should be JSON with the following format:
```json
{
{
"mode": "The verification mode, e.g. THREE_G",
"hcert": "The raw payload of the certificate QR code beginning with 'HC1:'"
}
```
The `mode` field must be one of the currently valid verification modes. A list of modes can be obtained with a `GET` request to the `v1/modes` endpoint:

```json
[
"THREE_G",
"TWO_G"
]
```


The endpoint will respond with status code 400 if the certificate couldn't be decoded. Otherwise, it will respond with status code 200 and the following JSON:
```json
Expand All @@ -54,128 +64,156 @@ The endpoint will respond with status code 400 if the certificate couldn't be de
"invalidState": "..."
}
```
where only one of the three fields `successState`, `errorState`, `invalidState` is set to a non-null value, thereby indicating the corresponding verification-status.
where only one of the three fields `successState`, `errorState`, `invalidState` is set to a non-null value, thereby indicating the corresponding verification-status. Since the introduction of different verification modes, the presence of the `successState` object is no longer sufficient to indicate the validity of a certificate. You must also check the value of the `successState.successState.modeValidity.modeValidityState` field. It can currently take one of the following values:

| Value | Meaning |
|--------------|----------------------------------------------------|
| SUCCESS | The certificate fulfills all criteria of this mode |
| INVALID | The certificate is not valid under this mode |
| UNKNOWN_MODE | An invalid verification mode was specified |

<details>
<summary>Examples</summary>

Request payload:
```json
{"hcert": "HC1:NCFJ60EG0/3WUWGSLKH47GO0KNJ9DSWQIIWT9CK+500XKY-CE59-G80:84F3ZKG%QU2F30GK JEY50.FK6ZK7:EDOLOPCF8F746KG7+59.Q6+A80:6JM8SX8RM8.A8TL6IA7-Q6.Q6JM8WJCT3EYM8XJC +DXJCCWENF6OF63W5$Q69L6%JC+QE$.32%E6VCHQEU$DE44NXOBJE719$QE0/D+8D-ED.24-G8$:8.JCBECB1A-:8$96646AL60A60S6Q$D.UDRYA 96NF6L/5QW6307KQEPD09WEQDD+Q6TW6FA7C466KCN9E%961A6DL6FA7D46JPCT3E5JDJA76L68463W5/A6..DX%DZJC3/DH$9- NTVDWKEI3DK2D4XOXVD1/DLPCG/DU2D4ZA2T9GY8MPCG/DY-CAY81C9XY8O/EZKEZ96446256V50G7AZQ4CUBCD9-FV-.6+OJROVHIBEI3KMU/TLRYPM0FA9DCTID.GQ$NYE3NPBP90/9IQH24YL7WMO0CNV1 SDB1AHX7:O26872.NV/LC+VJ75L%NGF7PT134ERGJ.I0 /49BB6JA7WKY:AL19PB120CUQ37XL1P9505-YEFJHVETB3CB-KE8EN9BPQIMPRTEW*DU+X2STCJ6O6S4XXVJ$UQNJW6IIO0X20D4S3AWSTHTA5FF7I/J9:8ALF/VP 4K1+8QGI:N0H 91QBHPJLSMNSJC BFZC5YSD.9-9E5R8-.IXUB-OG1RRQR7JEH/5T852EA3T7P6 VPFADBFUN0ZD93MQY07/4OH1FKHL9P95LIG841 BM7EXDR/PLCUUE88+-IX:Q"}
{"mode": "TWO_G", "hcert": "HC1:NCF260VG0/3WUWGSLKH47GO0.TSZV5ZBHU3M8CK8PV*70YM8FN0E$C$T1WY0-FCA0LD97TK0F90IECRHGWJC0FDL:4:KEPH7M/ESDD746KG7+592X61:6WA7Q46PF6XW6M*83:64R6XX8T%61A6WJCT3EYM8%JC+QE$.32%E6VCHQEU$DE44NXOBJE719$QE0/D+8D-ED.24-G8$:83KCZPCNF6OF64W5KF6-96/SA5R6%961G73564KCJPCITA2OA4KCD3DX47B46IL6646H*6KWEKDDC%6-Q6QW66464KCAWE6T9G%6G%67W5JPCT3E6JD646+/6C464W51S6..DX%DZJC1/DI-AXVD3VCI3DYUC6$C5WEW.C7WEV+A:S92T8I3D6WEITA2OA$PC5$CUZC$$5Y$5FBBY10D-ABIHJSEJVDKIDPRT/7D4BN/+40SDH4EIBR2:4$0G4MCT0N6BSWBVN.9AYA.UA4ANCEOUJEKOR04R36QT7A2I20KE JK5N47THHSU..7U05V/6UAKGZ3DPO.KB8-P2DM0RB:PC$65LHU3638$N WI*AP OPCGO:MAF4G*39D8T5C4BR3UMNB1R*Q8I5G35HCPC*V9OZJ0/K:OKTC1N E:JO17HKWQOR81JP0BCBL8%5IA6J8VJMM0L$60$BLNACPAN4NOKQ6XPC8Q2F9B-B$YGHDAFGKY$MS/I1YLYTVC$OHJAX LLKDLHGYAB-SON0A807AMH/CUPQ99TUXEAJ5486FQK1B%A2VM0C569O9AK-DH6MPU:UVPTCZLA:Q-+0EK8G4"}
```

Response for valid certificate:
```json
{
"certificate":{
"version":"1.0.0",
"person":{
"familyName":"vaccine",
"standardizedFamilyName":"VACCINE",
"givenName":"valid from today",
"standardizedGivenName":"VALID<FROM<TODAY"
},
"dateOfBirth":"15.01.1970",
"personName":{
"familyName":"vaccine",
"standardizedFamilyName":"VACCINE",
"givenName":"valid from today",
"standardizedGivenName":"VALID<FROM<TODAY"
},
"formattedDateOfBirth":"15.01.1970"
},
"successState":{
"isLightCertificate":false,
"validityRange":{
"validFrom":[
2021,
10,
13,
0,
0
],
"validUntil":[
2022,
10,
12,
0,
0
]
"certificate": {
"version": "1.0.0",
"person": {
"familyName": "Valid",
"standardizedFamilyName": "VALID",
"givenName": "Vaccine",
"standardizedGivenName": "VACCINE"
},
"dateOfBirth": "05.02.2003",
"personName": {
"familyName": "Valid",
"standardizedFamilyName": "VALID",
"givenName": "Vaccine",
"standardizedGivenName": "VACCINE"
},
"formattedDateOfBirth": "05.02.2003"
},
"successState": {
"successState": {
"modeValidity": {
"mode": "THREE_G",
"modeValidityState": "SUCCESS"
}
},
"errorState":null,
"invalidState":null
},
"isLightCertificate": false
},
"errorState": null,
"invalidState": null
}
```

Response for invalid certificate (e.g. expired)
```json
{
"certificate":{
"version":"1.0.0",
"person":{
"familyName":"vaccine",
"standardizedFamilyName":"VACCINE",
"givenName":"valid until today",
"standardizedGivenName":"VALID<UNTIL<TODAY"
},
"dateOfBirth":"15.01.1970",
"personName":{
"familyName":"vaccine",
"standardizedFamilyName":"VACCINE",
"givenName":"valid until today",
"standardizedGivenName":"VALID<UNTIL<TODAY"
},
"formattedDateOfBirth":"15.01.1970"
},
"successState":null,
"errorState":null,
"invalidState":{
"signatureState":{

},
"revocationState":{

},
"nationalRulesState":{
"validityRange":{
"validFrom":[
2020,
10,
13,
0,
0
],
"validUntil":[
2021,
10,
12,
0,
0
]
},
"ruleId":"VR-CH-0006"
"certificate": {
"version": "1.0.0",
"person": {
"familyName": "Expired",
"standardizedFamilyName": "EXPIRED",
"givenName": "Vaccine",
"standardizedGivenName": "VACCINE"
},
"dateOfBirth": "03.02.1999",
"personName": {
"familyName": "Expired",
"standardizedFamilyName": "EXPIRED",
"givenName": "Vaccine",
"standardizedGivenName": "VACCINE"
},
"formattedDateOfBirth": "03.02.1999"
},
"successState": null,
"errorState": null,
"invalidState": {
"signatureState": {},
"revocationState": {},
"nationalRulesState": {
"validityRange": {
"validFrom": [
2020,
12,
13,
0,
0
],
"validUntil": [
2021,
12,
12,
0,
0
]
},
"validityRange":{
"validFrom":[
2020,
10,
13,
0,
0
],
"validUntil":[
2021,
10,
12,
0,
0
]
}
}
"ruleId": "VR-CH-0006"
},
"validityRange": {
"validFrom": [
2020,
12,
13,
0,
0
],
"validUntil": [
2021,
12,
12,
0,
0
]
}
}
}
```

Response for a certificate that is basically valid (i.e. recognized in Switzerland and within its validity period), but not sufficient for the current verification mode, e.g. a test certificate in 2G (vaccine/recovery only) mode:

```json
{
"certificate": {
"version": "1.0.0",
"person": {
"familyName": "Valid",
"standardizedFamilyName": "VALID",
"givenName": "Test",
"standardizedGivenName": "TEST"
},
"dateOfBirth": "14.06.2007",
"personName": {
"familyName": "Valid",
"standardizedFamilyName": "VALID",
"givenName": "Test",
"standardizedGivenName": "TEST"
},
"formattedDateOfBirth": "14.06.2007"
},
"successState": {
"successState": {
"modeValidity": {
"mode": "TWO_G",
"modeValidityState": "INVALID"
}
},
"isLightCertificate": false
},
"errorState": null,
"invalidState": null
}
```
Pay special attention to the `successState` object. It is present, indicating that the certificate itself is valid, but the `modeValidityState` indicates that it is invalid under the selected mode.

</details>

## Contribution Guide
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ch.admin.bag.covidcertificate.backend.verification.check.model;

import javax.validation.constraints.NotNull;

public class SimpleControllerPayload extends HCertPayload {
@NotNull
private String mode;

public String getMode() {
return mode;
}

public void setMode(String mode) {
this.mode = mode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
<dependency>
<groupId>ch.admin.bag.covidcertificate</groupId>
<artifactId>sdk-core</artifactId>
<version>2.0.4</version>
<version>3.0</version>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
package ch.admin.bag.covidcertificate.backend.verification.check.ws.controller;

import ch.admin.bag.covidcertificate.backend.verification.check.model.HCertPayload;
import ch.admin.bag.covidcertificate.backend.verification.check.model.SimpleControllerPayload;
import ch.admin.bag.covidcertificate.backend.verification.check.ws.model.DecodingException;
import ch.admin.bag.covidcertificate.backend.verification.check.ws.model.SimpleVerificationResponse;
import ch.admin.bag.covidcertificate.backend.verification.check.ws.verification.VerificationService;
import ch.admin.bag.covidcertificate.sdk.core.models.state.VerificationState;
import ch.admin.bag.covidcertificate.sdk.core.models.state.VerificationState.ERROR;
import ch.admin.bag.covidcertificate.sdk.core.models.state.VerificationState.INVALID;
import ch.admin.bag.covidcertificate.sdk.core.models.trustlist.ActiveModes;
import ch.ubique.openapi.docannotations.Documentation;
import java.time.Instant;
import java.util.List;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
Expand Down Expand Up @@ -44,15 +48,18 @@ public SimpleController(VerificationService verificationService) {
})
@CrossOrigin(origins = {"https://editor.swagger.io"})
@PostMapping("/verify")
public @ResponseBody SimpleVerificationResponse verify(@RequestBody HCertPayload hCertPayload) {
public @ResponseBody SimpleVerificationResponse verify(@RequestBody SimpleControllerPayload hCertPayload) {
String verificationMode = hCertPayload.getMode();
final var start = Instant.now();

// Decode hcert
logger.info("Decoding hcert");
final var certificateHolder = verificationService.decodeHCert(hCertPayload);

logger.info("Verifying hcert");
// Verify hcert
final var verificationState = verificationService.verifyDcc(certificateHolder);
final var verificationState = verificationService.verifyDccSingleMode(certificateHolder,
verificationMode);

// Build response
final var simpleVerificationResponse =
Expand All @@ -71,7 +78,18 @@ public SimpleController(VerificationService verificationService) {
return simpleVerificationResponse;
}

@ExceptionHandler(DecodingException.class)
@Documentation(
description = "Get currently valid verification modes"
)
@CrossOrigin(origins = {"https://editor.swagger.io"})
@GetMapping("/modes")
public @ResponseBody List<String> getVerificationModes() {
return verificationService.getVerificationModes().stream().map(ActiveModes::getId).collect(
Collectors.toList());
}


@ExceptionHandler(DecodingException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<String> invalidHCert(DecodingException e) {
logger.info("Decoding exception thrown: {}", e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public VerificationController(VerificationService verificationService) {
final var certificateHolder = verificationService.decodeHCert(hCertPayload);

// Verify hcert
final var verificationState = verificationService.verifyDcc(certificateHolder);
final var verificationState = verificationService.verifyDccAllModes(certificateHolder);

// Build response
final var verificationResponse = new VerificationResponse();
Expand Down
Loading

0 comments on commit 333c5ce

Please sign in to comment.