Skip to content

Commit

Permalink
Add signature check for NRTMv4 client (#1596)
Browse files Browse the repository at this point in the history
* feat: add signature check for NRTMv4 client

* feat: get the UNF from signature

* feat: add wrong signed joses and apply to tests

* feat: use a file instead of a property because a possible key rotation

* feat: add static fields

* feat: log as error and not as info

* feat: change the mediaType to jose
  • Loading branch information
MiguelAHM authored Nov 26, 2024
1 parent 34451fa commit b424856
Show file tree
Hide file tree
Showing 13 changed files with 129 additions and 33 deletions.
10 changes: 10 additions & 0 deletions whois-nrtm4-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@
</dependency>


<!-- Signature -->
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
</dependency>
<dependency>
<groupId>com.google.crypto.tink</groupId>
<artifactId>tink</artifactId>
</dependency>

<!-- test -->
<dependency>
<groupId>net.ripe.db</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,17 @@ public List<String> getNrtmAvailableSources(){
}
}

public UpdateNotificationFileResponse getNotificationFile(final String source){
public String getNotificationFileSignature(final String source){
return client.target(String.format("%s/%s", baseUrl, source))
.path("update-notification-file.json")
.request(MediaType.APPLICATION_JSON_TYPE)
.get(UpdateNotificationFileResponse.class);
.path("update-notification-file.jose")
.request("application/jose+json")
.get(String.class);
}

@Nullable
public byte[] getSnapshotFile(final String url){
try {
final Response response = client.target(url)
final Response response = client.target(String.format("%s/%s", baseUrl, url))
.request(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeader.X_FORWARDED_PROTO.asString(), HttpScheme.HTTPS.asString())
.get(Response.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package net.ripe.db.nrtm4.client.processor;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.Ed25519Verifier;
import com.nimbusds.jose.jwk.OctetKeyPair;
import net.ripe.db.nrtm4.client.client.NrtmRestClient;
import net.ripe.db.nrtm4.client.client.UpdateNotificationFileResponse;
import net.ripe.db.nrtm4.client.condition.Nrtm4ClientCondition;
Expand All @@ -12,6 +19,11 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Nullable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
Expand All @@ -28,6 +40,8 @@ public class UpdateNotificationFileProcessor {

private final SnapshotImporter snapshotImporter;

private final static String PUBLIC_KEY_PATH = "public.key";

public UpdateNotificationFileProcessor(final NrtmRestClient nrtmRestClient,
final Nrtm4ClientMirrorRepository nrtm4ClientMirrorDao,
final SnapshotImporter snapshotImporter) {
Expand All @@ -38,25 +52,41 @@ public UpdateNotificationFileProcessor(final NrtmRestClient nrtmRestClient,

@Transactional(rollbackFor = Exception.class)
public void processFile(){
final Map<String, UpdateNotificationFileResponse> notificationFilePerSource =
final Map<String, String> notificationFilePerSource =
nrtmRestClient.getNrtmAvailableSources()
.stream()
.collect(Collectors.toMap(
string -> string,
nrtmRestClient::getNotificationFile
nrtmRestClient::getNotificationFileSignature
));
LOGGER.info("Succeeded to read notification files from {}", notificationFilePerSource.keySet());
final List<NrtmClientVersionInfo> nrtmLastVersionInfoPerSource = nrtm4ClientMirrorDao.getNrtmLastVersionInfoForUpdateNotificationFile();

//TODO: [MH] Review integrity of the data checking the signature using the public key

notificationFilePerSource.forEach((source, updateNotificationFile) -> {
notificationFilePerSource.forEach((source, updateNotificationSignature) -> {
final NrtmClientVersionInfo nrtmClientLastVersionInfo = nrtmLastVersionInfoPerSource
.stream()
.filter(nrtmVersionInfo -> nrtmVersionInfo.source() != null && nrtmVersionInfo.source().equals(source))
.findFirst()
.orElse(null);

final JWSObject jwsObjectParsed;
try {
jwsObjectParsed = JWSObject.parse(updateNotificationSignature);
} catch (ParseException e) {
return;
}

if (!isCorrectSignature(jwsObjectParsed)){
LOGGER.error("Update Notification File not corrected signed for {} source", source);
return;
}

final UpdateNotificationFileResponse updateNotificationFile = getUpdateNotificationFileResponse(jwsObjectParsed);

if (updateNotificationFile == null){
return;
}

if (nrtmClientLastVersionInfo != null && !nrtmClientLastVersionInfo.sessionID().equals(updateNotificationFile.getSessionID())){
LOGGER.info("Different session");
snapshotImporter.initializeNRTMClientForSource(source, updateNotificationFile);
Expand All @@ -83,4 +113,42 @@ public void processFile(){
});
}


@Nullable
private UpdateNotificationFileResponse getUpdateNotificationFileResponse(final JWSObject jwsObjectParsed) {
try {
String payloadJson = jwsObjectParsed.getPayload().toString();
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(payloadJson, UpdateNotificationFileResponse.class);
} catch (JsonProcessingException ex){
LOGGER.error("Unable to get the update notification file from the signature");
return null;
}
}

private boolean isCorrectSignature(final JWSObject jwsObjectParsed) {
try {
final OctetKeyPair parsedPublicKey = OctetKeyPair.parse(readPublicKey());

final JWSVerifier verifier = new Ed25519Verifier(parsedPublicKey);
return jwsObjectParsed.verify(verifier);
} catch (JOSEException | ParseException ex) {
LOGGER.error("failed to verify signature {}", ex.getMessage());
return false;
}
}

private static String readPublicKey() {
try {
try (InputStream inputStream = UpdateNotificationFileProcessor.class.getClassLoader().getResourceAsStream(PUBLIC_KEY_PATH)) {
if (inputStream == null) {
throw new FileNotFoundException("Public key file not found in resources: " + PUBLIC_KEY_PATH);
}
return new String(inputStream.readAllBytes());
}
} catch (IOException ex){
throw new IllegalStateException("Public key file not found in resources: " + PUBLIC_KEY_PATH);
}
}

}
1 change: 1 addition & 0 deletions whois-nrtm4-client/src/main/resources/public.key
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"kty":"OKP","crv":"Ed25519","kid":"a9ddf4a5-0ca0-47b1-a80d-3c63fd5c19c5","x":"ry9yLgcy1eUNX1lDs852mmUXRoy4qZW1HSOu54qBCHI"}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public void reset(){
@BeforeAll
public static void setUp(){
System.setProperty("nrtm4.client.enabled", "true");

}

@AfterAll
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package net.ripe.db.nrtm4.client;

import com.google.common.io.Resources;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.Payload;
import com.nimbusds.jose.crypto.Ed25519Signer;
import com.nimbusds.jose.jwk.OctetKeyPair;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -28,6 +36,7 @@
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.GZIPOutputStream;
Expand Down Expand Up @@ -57,13 +66,13 @@ public class NrtmServerDummy implements Stub {
"version": 1,
"snapshot": {
"version": 1,
"url": "http://localhost:%s/nrtmv4/RIPE/nrtm-snapshot.4.RIPE.4521174b-548f-4e51-98fc-dfd720011a0c.82542bd048e111fe57db404d08b6433e.json.gz",
"url": "RIPE/nrtm-snapshot.4.RIPE.4521174b-548f-4e51-98fc-dfd720011a0c.82542bd048e111fe57db404d08b6433e.json.gz",
"hash": "%s"
},
"deltas": [
{
"version": 1,
"url": "http://localhost:%s/nrtmv4/RIPE/nrtm-delta.4.RIPE.4521174b-548f-4e51-98fc-dfd720011a0c.e3be41ff312010046b67d099faa58f44.json",
"url": "RIPE/nrtm-delta.4.RIPE.4521174b-548f-4e51-98fc-dfd720011a0c.e3be41ff312010046b67d099faa58f44.json",
"hash": "c50dd7554cb35ef5f2f45d7bfa09fc51033cbe1152d29b36cb1178319e22be3e"
}
]
Expand All @@ -80,7 +89,7 @@ public class NrtmServerDummy implements Stub {
"version": 1,
"snapshot": {
"version": 1,
"url": "http://localhost:%s/nrtmv4/RIPE-NONAUTH/nrtm-snapshot.1.RIPE-NONAUTH.6328095e-7d46-415b-9333-8f2ae274b7c8.f1195bb8a666fe7b97fa74009a70cefa.json.gz",
"url": "RIPE-NONAUTH/nrtm-snapshot.1.RIPE-NONAUTH.6328095e-7d46-415b-9333-8f2ae274b7c8.f1195bb8a666fe7b97fa74009a70cefa.json.gz",
"hash": "%s"
},
"deltas": []
Expand Down Expand Up @@ -155,21 +164,29 @@ public void resetDefaultMocks(){
public void setFakeHashMocks(){
mocks.clear();
mocks.add(new NrtmResponseMock("/nrtmv4", "nrtm-sources.html", "application/html"));
mocks.add(new NrtmResponseMock("/nrtmv4/RIPE-NONAUTH/update-notification-file.json", getFakeUpdateNotificationNonAuthResponse(), "application/json"));
mocks.add(new NrtmResponseMock("/nrtmv4/RIPE/update-notification-file.json", getFakeUpdateNotificationRipeResponse(), "application/json"));
mocks.add(new NrtmResponseMock("/nrtmv4/RIPE/update-notification-file.jose", "fake-nrtm-RIPE-signature.jose", "application/jose+json"));
mocks.add(new NrtmResponseMock("/nrtmv4/RIPE-NONAUTH/update-notification-file.jose", "fake-nrtm-RIPE-NONAUTH-signature.jose", "application/jose+json"));
mocks.add(new NrtmCompressedResponseMock("/nrtmv4/RIPE-NONAUTH/nrtm-snapshot.1.RIPE-NONAUTH.6328095e-7d46-415b-9333-8f2ae274b7c8.f1195bb8a666fe7b97fa74009a70cefa.json.gz", "nrtm-snapshot.1.RIPE-NONAUTH.json"));
mocks.add(new NrtmCompressedResponseMock("/nrtmv4/RIPE/nrtm-snapshot.4.RIPE.4521174b-548f-4e51-98fc-dfd720011a0c.82542bd048e111fe57db404d08b6433e.json.gz", "nrtm-snapshot.1.RIPE.json"));
}

public void setWrongSignedUNF(){
mocks.clear();
mocks.add(new NrtmResponseMock("/nrtmv4", "nrtm-sources.html", "application/html"));
mocks.add(new NrtmResponseMock("/nrtmv4/RIPE/update-notification-file.jose", "nrtm-RIPE-wrong-signature.jose", "application/jose+json"));
mocks.add(new NrtmResponseMock("/nrtmv4/RIPE-NONAUTH/update-notification-file.jose", "nrtm-RIPE-NONAUTH-wrong-signature.jose", "application/jose+json"));
}

private void initialiseMocks() {
mocks.add(new NrtmResponseMock("/nrtmv4", "nrtm-sources.html", "application/html"));
mocks.add(new NrtmResponseMock("/nrtmv4/RIPE-NONAUTH/update-notification-file.json", getUpdateNotificationFileNonAuthResponse(), "application/json"));
mocks.add(new NrtmResponseMock("/nrtmv4/RIPE/update-notification-file.json", getUpdateNotificationFileRipeResponse(), "application/json"));
mocks.add(new NrtmResponseMock("/nrtmv4/RIPE/update-notification-file.jose", "nrtm-RIPE-signature.jose", "application/jose+json"));
mocks.add(new NrtmResponseMock("/nrtmv4/RIPE-NONAUTH/update-notification-file.jose", "nrtm-RIPE-NONAUTH-signature.jose", "application/jose+json"));
mocks.add(new NrtmCompressedResponseMock("/nrtmv4/RIPE-NONAUTH/nrtm-snapshot.1.RIPE-NONAUTH.6328095e-7d46-415b-9333-8f2ae274b7c8.f1195bb8a666fe7b97fa74009a70cefa.json.gz", "nrtm-snapshot.1.RIPE-NONAUTH.json"));
mocks.add(new NrtmCompressedResponseMock("/nrtmv4/RIPE/nrtm-snapshot.4.RIPE.4521174b-548f-4e51-98fc-dfd720011a0c.82542bd048e111fe57db404d08b6433e.json.gz", "nrtm-snapshot.1.RIPE.json"));
}



private interface Mock {

String PATH = "mock/";
Expand Down Expand Up @@ -261,20 +278,4 @@ public Object response() {
return response;
}
}

private String getFakeUpdateNotificationRipeResponse(){
return String.format(unfRipeTemplate, port, "fake_hash", port);
}

private String getFakeUpdateNotificationNonAuthResponse(){
return String.format(unfRipeNonAuthTemplate, port, "fake_hash");
}

private String getUpdateNotificationFileRipeResponse(){
return String.format(unfRipeTemplate, port, "b293e92997d3be7a5156fdca832af378c3989b2cefa9e3e37caaeeba0ca971e9", port);
}

private String getUpdateNotificationFileNonAuthResponse(){
return String.format(unfRipeNonAuthTemplate, port, "148c3c411b8f044f5fc0ab201f6dd03e80c862e27ad1a63488aee337dc7eb4a2");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ public void UNF_with_different_hash_then_no_snapshot(){
assertThat(snapshotVersionPerSource, is(empty()));
}

@Test
public void wrongly_signed_UNF_then_no_UNF_added(){
nrtmServerDummy.setWrongSignedUNF();
updateNotificationFileProcessor.processFile();

final List<NrtmClientVersionInfo> versionInfosPerSource = nrtm4ClientMirrorRepository.getNrtmLastVersionInfoForUpdateNotificationFile();
assertThat(versionInfosPerSource, is(empty()));
}

@Test
public void process_UNF_but_DB_Ahead_Then_ReInitialize(){
// TODO: [MH] Re-initialize
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJraWQiOiJhOWRkZjRhNS0wY2EwLTQ3YjEtYTgwZC0zYzYzZmQ1YzE5YzUiLCJhbGciOiJFZDI1NTE5In0.ewogICJucnRtX3ZlcnNpb24iOiAxLAogICJ0aW1lc3RhbXAiOiAiMjAyNC0xMC0yNFQxMzoyMDowMFoiLAogICJ0eXBlIjogIm5vdGlmaWNhdGlvbiIsCiAgInNvdXJjZSI6ICJSSVBFLU5PTkFVVEgiLAogICJzZXNzaW9uX2lkIjogIjYzMjgwOTVlLTdkNDYtNDE1Yi05MzMzLThmMmFlMjc0YjdjOCIsCiAgInZlcnNpb24iOiAxLAogICJzbmFwc2hvdCI6IHsKICAgICJ2ZXJzaW9uIjogMSwKICAgICJ1cmwiOiAiUklQRS1OT05BVVRIL25ydG0tc25hcHNob3QuMS5SSVBFLU5PTkFVVEguNjMyODA5NWUtN2Q0Ni00MTViLTkzMzMtOGYyYWUyNzRiN2M4LmYxMTk1YmI4YTY2NmZlN2I5N2ZhNzQwMDlhNzBjZWZhLmpzb24uZ3oiLAogICAgImhhc2giOiAiZmFrZV9oYXNoIgogIH0sCiAgImRlbHRhcyI6IFtdCn0K.ByryeIsmJSVpuHg0nGnn06VsufJ60wdO5UHHiWjm_FzAThtAICZ8uiUkeiNDmrp0fpRWapH5CeTlxhdCfMqwDg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJraWQiOiJhOWRkZjRhNS0wY2EwLTQ3YjEtYTgwZC0zYzYzZmQ1YzE5YzUiLCJhbGciOiJFZDI1NTE5In0.ewogICJucnRtX3ZlcnNpb24iOiA0LAogICJ0aW1lc3RhbXAiOiAiMjAyNC0xMC0yNVQwMDowNzowMFoiLAogICJ0eXBlIjogIm5vdGlmaWNhdGlvbiIsCiAgInNvdXJjZSI6ICJSSVBFIiwKICAic2Vzc2lvbl9pZCI6ICI0NTIxMTc0Yi01NDhmLTRlNTEtOThmYy1kZmQ3MjAwMTFhMGMiLAogICJ2ZXJzaW9uIjogMSwKICAic25hcHNob3QiOiB7CiAgICAidmVyc2lvbiI6IDEsCiAgICAidXJsIjogIlJJUEUvbnJ0bS1zbmFwc2hvdC40LlJJUEUuNDUyMTE3NGItNTQ4Zi00ZTUxLTk4ZmMtZGZkNzIwMDExYTBjLjgyNTQyYmQwNDhlMTExZmU1N2RiNDA0ZDA4YjY0MzNlLmpzb24uZ3oiLAogICAgImhhc2giOiAiZmFrZV9oYXNoIgogIH0sCiAgImRlbHRhcyI6IFsKICAgIHsKICAgICAgInZlcnNpb24iOiAxLAogICAgICAidXJsIjogIlJJUEUvbnJ0bS1kZWx0YS40LlJJUEUuNDUyMTE3NGItNTQ4Zi00ZTUxLTk4ZmMtZGZkNzIwMDExYTBjLmUzYmU0MWZmMzEyMDEwMDQ2YjY3ZDA5OWZhYTU4ZjQ0Lmpzb24iLAogICAgICAiaGFzaCI6ICJjNTBkZDc1NTRjYjM1ZWY1ZjJmNDVkN2JmYTA5ZmM1MTAzM2NiZTExNTJkMjliMzZjYjExNzgzMTllMjJiZTNlIgogICAgfQogIF0KfQo.FleGXqvep1AmVmnn3Hk9oU0eGNEOtE8vEY19tKVdpJMSHAijOtzWcyasVI1vHA3vGxj7jaxqm-l3LnG6ZoyDAw
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJraWQiOiJhOWRkZjRhNS0wY2EwLTQ3YjEtYTgwZC0zYzYzZmQ1YzE5YzUiLCJhbGciOiJFZDI1NTE5In0.ewogICJucnRtX3ZlcnNpb24iOiAxLAogICJ0aW1lc3RhbXAiOiAiMjAyNC0xMC0yNFQxMzoyMDowMFoiLAogICJ0eXBlIjogIm5vdGlmaWNhdGlvbiIsCiAgInNvdXJjZSI6ICJSSVBFLU5PTkFVVEgiLAogICJzZXNzaW9uX2lkIjogIjYzMjgwOTVlLTdkNDYtNDE1Yi05MzMzLThmMmFlMjc0YjdjOCIsCiAgInZlcnNpb24iOiAxLAogICJzbmFwc2hvdCI6IHsKICAgICJ2ZXJzaW9uIjogMSwKICAgICJ1cmwiOiAiUklQRS1OT05BVVRIL25ydG0tc25hcHNob3QuMS5SSVBFLU5PTkFVVEguNjMyODA5NWUtN2Q0Ni00MTViLTkzMzMtOGYyYWUyNzRiN2M4LmYxMTk1YmI4YTY2NmZlN2I5N2ZhNzQwMDlhNzBjZWZhLmpzb24uZ3oiLAogICAgImhhc2giOiAiMTQ4YzNjNDExYjhmMDQ0ZjVmYzBhYjIwMWY2ZGQwM2U4MGM4NjJlMjdhZDFhNjM0ODhhZWUzMzdkYzdlYjRhMiIKICB9LAogICJkZWx0YXMiOiBbXQp9Cg.AGsn3z7Yd_-unMprcjUMB5nBkqdPrQuiC2C494Joz7CoGxfJEc9zWsVQ28nlK3GJVwMtSBCWP8msPEV_lX_lCg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJraWQiOiJhOWRkZjRhNS0wY2EwLTQ3YjEtYTgwZC0zYzYzZmQ1YzE5YzUiLCJhbGciOiJFZDI1NTE5In0.ewogICJucnRtX3ZlcnNpb24iOiAxLAogICJ0aW1lc3RhbXAiOiAiMjAyNC0xMC0yNFQxMzoyMDowMFoiLAogICJ0eXBlIjogIm5vdGlmaWNhdGlvbiIsCiAgInNvdXJjZSI6ICJSSVBFLU5PTkFVVEgiLAogICJzZXNzaW9uX2lkIjogIjYzMjgwOTVlLTdkNDYtNDE1Yi05MzMzLThmMmFlMjc0YjdjOCIsCiAgInZlcnNpb24iOiAxLAogICJzbmFwc2hvdCI6IHsKICAgICJ2ZXJzaW9uIjogMSwKICAgICJ1cmwiOiAiUklQRS1OT05BVVRIL25ydG0tc25hcHNob3QuMS5SSVBFLU5PTkFVVEguNjMyODA5NWUtN2Q0Ni00MTViLTkzMzMtOGYyYWUyNzRiN2M4LmYxMTk1YmI4YTY2NmZlN2I5N2ZhNzQwMDlhNzBjZWZhLmpzb24uZ3oiLAogICAgImhhc2giOiAiMTQ4YzNjNDExYjhmMDQ0ZjVmYzBhYjIwMWY2ZGQwM2U4MGM4NjJlMjdhZDFhNjM0ODhhZWUzMzdkYzdlYjRhMiIKICB9LAogICJkZWx0YXMiOiBbXQp9Cg.AGsn3z7Yd_-unMprcjUMB5nBkqdPrQuiC2C494Joz7CoGxfJEc9zWsVQ28nlK3GJVwMtSBCWP8msPEV_lX_lCF
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJraWQiOiJhOWRkZjRhNS0wY2EwLTQ3YjEtYTgwZC0zYzYzZmQ1YzE5YzUiLCJhbGciOiJFZDI1NTE5In0.ewogICJucnRtX3ZlcnNpb24iOiA0LAogICJ0aW1lc3RhbXAiOiAiMjAyNC0xMC0yNVQwMDowNzowMFoiLAogICJ0eXBlIjogIm5vdGlmaWNhdGlvbiIsCiAgInNvdXJjZSI6ICJSSVBFIiwKICAic2Vzc2lvbl9pZCI6ICI0NTIxMTc0Yi01NDhmLTRlNTEtOThmYy1kZmQ3MjAwMTFhMGMiLAogICJ2ZXJzaW9uIjogMSwKICAic25hcHNob3QiOiB7CiAgICAidmVyc2lvbiI6IDEsCiAgICAidXJsIjogIlJJUEUvbnJ0bS1zbmFwc2hvdC40LlJJUEUuNDUyMTE3NGItNTQ4Zi00ZTUxLTk4ZmMtZGZkNzIwMDExYTBjLjgyNTQyYmQwNDhlMTExZmU1N2RiNDA0ZDA4YjY0MzNlLmpzb24uZ3oiLAogICAgImhhc2giOiAiYjI5M2U5Mjk5N2QzYmU3YTUxNTZmZGNhODMyYWYzNzhjMzk4OWIyY2VmYTllM2UzN2NhYWVlYmEwY2E5NzFlOSIKICB9LAogICJkZWx0YXMiOiBbCiAgICB7CiAgICAgICJ2ZXJzaW9uIjogMSwKICAgICAgInVybCI6ICJSSVBFL25ydG0tZGVsdGEuNC5SSVBFLjQ1MjExNzRiLTU0OGYtNGU1MS05OGZjLWRmZDcyMDAxMWEwYy5lM2JlNDFmZjMxMjAxMDA0NmI2N2QwOTlmYWE1OGY0NC5qc29uIiwKICAgICAgImhhc2giOiAiYzUwZGQ3NTU0Y2IzNWVmNWYyZjQ1ZDdiZmEwOWZjNTEwMzNjYmUxMTUyZDI5YjM2Y2IxMTc4MzE5ZTIyYmUzZSIKICAgIH0KICBdCn0K.PPu7vdJJW-8h1GsLlXKhGCmzkUt_cTQVpC9WLvhSFlfbM78kJYLFO_9YgaA-0oRaBfSmprCBUA3PZoP6UK3mAg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJraWQiOiJhOWRkZjRhNS0wY2EwLTQ3YjEtYTgwZC0zYzYzZmQ1YzE5YzUiLCJhbGciOiJFZDI1NTE5In0.ewogICJucnRtX3ZlcnNpb24iOiA0LAogICJ0aW1lc3RhbXAiOiAiMjAyNC0xMC0yNVQwMDowNzowMFoiLAogICJ0eXBlIjogIm5vdGlmaWNhdGlvbiIsCiAgInNvdXJjZSI6ICJSSVBFIiwKICAic2Vzc2lvbl9pZCI6ICI0NTIxMTc0Yi01NDhmLTRlNTEtOThmYy1kZmQ3MjAwMTFhMGMiLAogICJ2ZXJzaW9uIjogMSwKICAic25hcHNob3QiOiB7CiAgICAidmVyc2lvbiI6IDEsCiAgICAidXJsIjogIlJJUEUvbnJ0bS1zbmFwc2hvdC40LlJJUEUuNDUyMTE3NGItNTQ4Zi00ZTUxLTk4ZmMtZGZkNzIwMDExYTBjLjgyNTQyYmQwNDhlMTExZmU1N2RiNDA0ZDA4YjY0MzNlLmpzb24uZ3oiLAogICAgImhhc2giOiAiYjI5M2U5Mjk5N2QzYmU3YTUxNTZmZGNhODMyYWYzNzhjMzk4OWIyY2VmYTllM2UzN2NhYWVlYmEwY2E5NzFlOSIKICB9LAogICJkZWx0YXMiOiBbCiAgICB7CiAgICAgICJ2ZXJzaW9uIjogMSwKICAgICAgInVybCI6ICJSSVBFL25ydG0tZGVsdGEuNC5SSVBFLjQ1MjExNzRiLTU0OGYtNGU1MS05OGZjLWRmZDcyMDAxMWEwYy5lM2JlNDFmZjMxMjAxMDA0NmI2N2QwOTlmYWE1OGY0NC5qc29uIiwKICAgICAgImhhc2giOiAiYzUwZGQ3NTU0Y2IzNWVmNWYyZjQ1ZDdiZmEwOWZjNTEwMzNjYmUxMTUyZDI5YjM2Y2IxMTc4MzE5ZTIyYmUzZSIKICAgIH0KICBdCn0K.PPu7vdJJW-8h1GsLlXKhGCmzkUt_cTQVpC9WLvhSFlfbM78kJYLFO_9YgaA-0oRaBfSmprCBUA3PZoP6UK3mAF

0 comments on commit b424856

Please sign in to comment.