Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import com.uid2.shared.cloud.TaggableCloudStorage;
import com.uid2.shared.encryption.AesGcm;
import com.uid2.shared.model.CloudEncryptionKey;
import com.uid2.shared.model.SaltEntry;
import com.uid2.shared.store.CloudPath;
import com.uid2.shared.store.salt.RotatingSaltProvider;
import com.uid2.shared.store.reader.RotatingCloudEncryptionKeyProvider;
Expand All @@ -15,10 +14,7 @@
import org.slf4j.LoggerFactory;
import io.vertx.core.json.JsonObject;

import java.io.BufferedWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

public class EncryptedSaltStoreWriter extends SaltStoreWriter implements StoreWriter {
Expand All @@ -37,6 +33,19 @@ public EncryptedSaltStoreWriter(JsonObject config, RotatingSaltProvider provider
this.siteId = siteId;
}

@Override
public void upload(Object data, JsonObject extraMeta) throws Exception {
this.unEncryptedMetadataData = extraMeta;
@SuppressWarnings("unchecked")
List<RotatingSaltProvider.SaltSnapshot> snapshots = new ArrayList<>((Collection<RotatingSaltProvider.SaltSnapshot>) data);
this.buildAndUploadMetadata(snapshots);
}

@Override
public void rewriteMeta() throws Exception {
// No rewrite_metadata endpoint for salts
}

@Override
protected java.lang.String getSaltSnapshotLocation(RotatingSaltProvider.SaltSnapshot snapshot) {
return scope.resolve(new CloudPath("salts.txt." + snapshot.getEffective().toEpochMilli())).toString();
Expand Down Expand Up @@ -82,13 +91,16 @@ protected boolean tryUploadSaltsSnapshot(RotatingSaltProvider.SaltSnapshot snaps
return false;
}
}
StringBuilder stringBuilder = new StringBuilder();

for (SaltEntry entry: snapshot.getAllRotatingSalts()) {
stringBuilder.append(entry.id()).append(",").append(entry.lastUpdated()).append(",").append(entry.currentSalt()).append("\n");
}
var saltCsv = SaltSerializer.toCsv(snapshot.getAllRotatingSalts());
var encryptedSaltCsv = addEncryptedEnvelope(encryptionKey, saltCsv);
uploadSaltsFile(location, encryptedSaltCsv);

LOGGER.info("File encryption completed for site_id={} key_id={} store={}", siteId, encryptionKey.getId(), "salts");
return true;
}

String data = stringBuilder.toString();
private String addEncryptedEnvelope(CloudEncryptionKey encryptionKey, String data) {
JsonObject encryptedJson = new JsonObject();
if (encryptionKey != null) {
byte[] secret = Base64.getDecoder().decode(encryptionKey.getSecret());
Expand All @@ -100,13 +112,7 @@ protected boolean tryUploadSaltsSnapshot(RotatingSaltProvider.SaltSnapshot snaps
throw new IllegalStateException("No Cloud Encryption keys available for encryption for site ID: " + siteId);
}

final Path newSaltsFile = Files.createTempFile("salts", ".txt");
try (BufferedWriter w = Files.newBufferedWriter(newSaltsFile)) {
w.write(encryptedJson.encodePrettily());
}
this.upload(newSaltsFile.toString(), location);
LOGGER.info("File encryption completed for site_id={} key_id={} store={}", siteId, encryptionKey.getId(), "salts");
return true;
return encryptedJson.encodePrettily();
}

@Override
Expand All @@ -118,17 +124,4 @@ protected JsonObject getMetadata(){
protected Long getMetadataVersion() throws Exception {
return this.unEncryptedMetadataData.getLong("version");
}

@Override
public void upload(Object data, JsonObject extraMeta) throws Exception {
this.unEncryptedMetadataData = extraMeta;
@SuppressWarnings("unchecked")
List<RotatingSaltProvider.SaltSnapshot> snapshots = new ArrayList<>((Collection<RotatingSaltProvider.SaltSnapshot>) data);
this.buildAndUploadMetadata(snapshots);
}

@Override
public void rewriteMeta() throws Exception {

}
}
56 changes: 56 additions & 0 deletions src/main/java/com/uid2/admin/store/writer/SaltSerializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.uid2.admin.store.writer;

import com.uid2.shared.model.SaltEntry;

public class SaltSerializer {
private SaltSerializer() {}

public static String toCsv(SaltEntry[] entries) {
StringBuilder stringBuilder = new StringBuilder();

for (SaltEntry entry : entries) {
addLine(entry, stringBuilder);
}

return stringBuilder.toString();
}

private static void addLine(SaltEntry entry, StringBuilder stringBuilder) {
stringBuilder
.append(entry.id())
.append(",")
.append(entry.lastUpdated())
.append(",")
.append(entry.currentSalt());

stringBuilder.append(",");
stringBuilder.append(serializeNullable(entry.refreshFrom()));

stringBuilder.append(",");
stringBuilder.append(serializeNullable(entry.previousSalt()));

appendKey(stringBuilder, entry.currentKey());
appendKey(stringBuilder, entry.previousKey());

stringBuilder.append("\n");
}

private static void appendKey(StringBuilder stringBuilder, SaltEntry.KeyMaterial key) {
if (key != null) {
stringBuilder
.append(",")
.append(key.id())
.append(",")
.append(serializeNullable(key.key()))
.append(",")
.append(serializeNullable(key.salt()));
}
else {
stringBuilder.append(",,,");
}
}

public static <T> String serializeNullable(T obj) {
return obj == null ? "" : obj.toString();
}
}
66 changes: 34 additions & 32 deletions src/main/java/com/uid2/admin/store/writer/SaltStoreWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.uid2.admin.store.FileManager;
import com.uid2.admin.store.version.VersionGenerator;
import com.uid2.shared.Utils;
import com.uid2.shared.cloud.CloudStorageException;
import com.uid2.shared.cloud.TaggableCloudStorage;
import com.uid2.shared.model.SaltEntry;
Expand All @@ -14,15 +13,13 @@
import org.slf4j.LoggerFactory;

import java.io.BufferedWriter;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class SaltStoreWriter {
Expand All @@ -45,6 +42,30 @@ public SaltStoreWriter(JsonObject config, RotatingSaltProvider provider, FileMan
this.versionGenerator = versionGenerator;
}

public void upload(RotatingSaltProvider.SaltSnapshot data) throws Exception {
this.buildAndUploadMetadata(this.getSnapshots(data));
refreshProvider();
}

/**
* reads the metadata file, and marks each referenced file as ready for archiving
*/
public void archiveSaltLocations() throws Exception {
final JsonObject metadata = provider.getMetadata();

metadata.getJsonArray("salts").forEach(instance -> {
try {
JsonObject salt = (JsonObject) instance;
String location = salt.getString("location", "");
if (!location.isBlank()) {
this.setStatusTagToObsolete(location);
}
} catch (Exception ex) {
LOGGER.error("Error marking object as ready for archiving", ex);
}
});
}

private List<RotatingSaltProvider.SaltSnapshot> getSnapshots(RotatingSaltProvider.SaltSnapshot data){
if (provider.getSnapshots() == null) {
throw new IllegalStateException("Snapshots cannot be null");
Expand Down Expand Up @@ -126,6 +147,7 @@ protected void buildAndUploadMetadata(List<RotatingSaltProvider.SaltSnapshot> sn
protected JsonObject enrichMetadata(JsonObject metadata){
return metadata;
}

/**
* Builds snapshot metadata and uploads snapshots if they need to be updated.
* <p>
Expand All @@ -151,34 +173,10 @@ private JsonArray uploadAndGetSnapshotsMetadata(List<RotatingSaltProvider.SaltSn
return anyUploadSucceeded ? snapshotsMetadata : new JsonArray();
}

public void upload(RotatingSaltProvider.SaltSnapshot data) throws Exception {
this.buildAndUploadMetadata(this.getSnapshots(data));
refreshProvider();
}

private void refreshProvider() throws Exception {
provider.loadContent();
}

/**
* reads the metadata file, and marks each referenced file as ready for archiving
*/
public void archiveSaltLocations() throws Exception {
final JsonObject metadata = provider.getMetadata();

metadata.getJsonArray("salts").forEach(instance -> {
try {
JsonObject salt = (JsonObject) instance;
String location = salt.getString("location", "");
if (!location.isBlank()) {
this.setStatusTagToObsolete(location);
}
} catch (Exception ex) {
LOGGER.error("Error marking object as ready for archiving", ex);
}
});
}

protected String getSaltSnapshotLocation(RotatingSaltProvider.SaltSnapshot snapshot) {
return saltSnapshotLocationPrefix + snapshot.getEffective().toEpochMilli();
}
Expand All @@ -200,14 +198,18 @@ protected boolean tryUploadSaltsSnapshot(RotatingSaltProvider.SaltSnapshot snaps
return false;
}

final Path newSaltsFile = Files.createTempFile("operators", ".txt");
var saltCsv = SaltSerializer.toCsv(snapshot.getAllRotatingSalts());
uploadSaltsFile(location, saltCsv);

return true;
}

protected void uploadSaltsFile(String location, String data) throws Exception {
final Path newSaltsFile = Files.createTempFile("salts", ".txt");
try (BufferedWriter w = Files.newBufferedWriter(newSaltsFile)) {
for (SaltEntry entry : snapshot.getAllRotatingSalts()) {
w.write(entry.id() + "," + entry.lastUpdated() + "," + entry.currentSalt() + "\n");
}
w.write(data);
}
this.upload(newSaltsFile.toString(), location);
return true;
}

protected void upload(String data, String location) throws Exception {
Expand Down
Loading