Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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,6 +6,7 @@
import com.uid2.shared.auth.RotatingOperatorKeyProvider;
import com.uid2.shared.model.CloudEncryptionKey;
import com.uid2.shared.store.reader.RotatingCloudEncryptionKeyProvider;
import io.vertx.core.json.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -34,6 +35,15 @@ public CloudEncryptionKeyManager(
this.planner = planner;
}

public JsonObject getMetadata() throws Exception {
return keyProvider.getMetadata();
}

public Set<CloudEncryptionKeySummary> getKeySummaries() throws Exception {
refreshCloudData();
return existingKeys.stream().map(CloudEncryptionKeySummary::fromFullKey).collect(Collectors.toSet());
}

// For any site that has an operator create a new key activating in one hour
// Keep up to 10 most recent old keys per site, delete the rest
public void rotateKeys(boolean shouldFail) throws Exception {
Expand Down Expand Up @@ -66,11 +76,6 @@ public void backfillKeys() throws Exception {
}
}

public Set<CloudEncryptionKeySummary> getKeySummaries() throws Exception {
refreshCloudData();
return existingKeys.stream().map(CloudEncryptionKeySummary::fromFullKey).collect(Collectors.toSet());
}

private void writeKeys(Set<CloudEncryptionKey> desiredKeys) throws Exception {
var keysForWriting = desiredKeys.stream().collect(Collectors.toMap(
CloudEncryptionKey::getId,
Expand All @@ -85,4 +90,4 @@ private void refreshCloudData() throws Exception {
operatorKeys = new HashSet<>(operatorKeyProvider.getAll());
existingKeys = new HashSet<>(keyProvider.getAll().values());
}
}
}
1 change: 1 addition & 0 deletions src/main/java/com/uid2/admin/vertx/Endpoints.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ public enum Endpoints {
API_SITE_APP_NAMES("/api/site/app_names"),
API_SITE_UPDATE("/api/site/update"),

CLOUD_ENCRYPTION_KEY_METADATA("/api/cloud-encryption-key/metadata"),
CLOUD_ENCRYPTION_KEY_LIST("/api/cloud-encryption-key/list"),
CLOUD_ENCRYPTION_KEY_ROTATE("/api/cloud-encryption-key/rotate"),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public CloudEncryptionKeyService(

@Override
public void setupRoutes(Router router) {
router.get(Endpoints.CLOUD_ENCRYPTION_KEY_METADATA.toString()).handler(
auth.handle(this::handleMetadata, Role.MAINTAINER));

router.get(Endpoints.CLOUD_ENCRYPTION_KEY_LIST.toString()).handler(
auth.handle(this::handleList, Role.MAINTAINER)
);
Expand All @@ -40,6 +43,25 @@ public void setupRoutes(Router router) {
);
}

private void handleMetadata(RoutingContext rc) {
try {
rc.response()
.putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
.end(keyManager.getMetadata().encode());
} catch (Exception e) {
rc.fail(500, e);
}
}

private void handleList(RoutingContext rc) {
try {
var response = new CloudEncryptionKeyListResponse(keyManager.getKeySummaries());
respondWithJson(rc, response);
} catch (Exception e) {
rc.fail(500, e);
}
}

private void handleRotate(RoutingContext rc) {
try {
var shouldFail = !rc.queryParam("fail").isEmpty();
Expand All @@ -59,15 +81,6 @@ private void handleRotate(RoutingContext rc) {
}
}

private void handleList(RoutingContext rc) {
try {
var response = new CloudEncryptionKeyListResponse(keyManager.getKeySummaries());
respondWithJson(rc, response);
} catch (Exception e) {
rc.fail(500, e);
}
}

private static void respondWithJson(RoutingContext rc, CloudEncryptionKeyListResponse response) throws JsonProcessingException {
rc.response()
.putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public EncryptedFilesSyncService(
this.jobDispatcher = jobDispatcher;
this.writeLock = writeLock;
this.config = config;
this.cloudEncryptionKeyProvider =cloudEncryptionKeyProvider;
this.cloudEncryptionKeyProvider = cloudEncryptionKeyProvider;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.oauth2.OAuth2Auth;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.handler.AuthenticationHandler;
import io.vertx.ext.web.handler.impl.OAuth2AuthHandlerImpl;
Expand Down
160 changes: 160 additions & 0 deletions webroot/adm/cloud-encryption-key.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="/js/main.js"></script>
<script src="https://unpkg.com/gridjs/dist/gridjs.umd.js"></script>
<link href="https://unpkg.com/gridjs/dist/theme/mermaid.min.css" rel="stylesheet" />
<style>
.active { color: darkgreen; }
.inactive { color: darkred; }
</style>
</head>
<body>
<h1>UID2 Env - Cloud Encryption Key Management</h1>

<a href="/">Back</a>

<br>
<br>

<div class="ro-adm" style="display: none">
<h3>Operations</h3>
<ul>
<li><a href="#" id="doMeta">Get Metadata</a></li>
<li><a href="#" id="doList">List Cloud Encryption Keys</a></li>
</ul>

<br>

<h3>Output</h3>
<div id="output">
<pre id="errorOutput"></pre>
<pre id="standardOutput"></pre>
</div>
<div id="output-table"></div>
</div>

<script language="JavaScript">
const grid = new gridjs.Grid({
columns: [
{
name: "Key ID",
formatter: (cell, row) => {
return gridjs.html(`<span class="${row.cells[4].data}">${cell}</span>`)
}
},
{
name: "Site",
formatter: (cell, row) => {
return gridjs.html(`<span class="${row.cells[4].data}">${cell.siteId} - ${cell.siteName}</span>`)
}
},
{
name: "Activates",
formatter: (cell, row) => {
return gridjs.html(`<span class="${row.cells[4].data}">${formatDate(cell)}</span>`)
}
},
{
name: "Created",
formatter: (cell, row) => {
return gridjs.html(`<span class="${row.cells[4].data}">${formatDate(cell)}</span>`)
}
},
{
name: "Style",
hidden: true
}
],
data: [],
sort: true,
search: {
selector: (cell, rowIndex, cellIndex) => {
if (cellIndex === 0) {
return cell;
} else if (cellIndex === 1) {
return `${cell.siteId} - ${cell.siteName}`;
}
}
},
language: {
search: {
placeholder: "Search by Key ID or Site..."
}
}
})
.render(document.getElementById("output-table"));

const formatDate = (text) => {
const date = new Date(text);
const options = {
year: "numeric",
month: "long",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
timeZoneName: "short"
};
return date.toLocaleString("en-US", options);
}

const updateGrid = (grid, data) => {
const groupedData = data.cloudEncryptionKeys.reduce((acc, key) => {
if (!acc[key.siteId]) {
acc[key.siteId] = [];
}
acc[key.siteId].push(key);
return acc;
}, {});

const gridData = [];
for (const siteId in groupedData) {
const keys = groupedData[siteId];
const latestKey = keys.reduce((latest, key) => new Date(key.activates) > new Date(latest.activates) ? key : latest);

keys.forEach((key) => {
let style = key === latestKey ? "active" : "inactive";

gridData.push([ key.id, { siteId: key.siteId, siteName: key.siteName }, key.activates, key.created, style ]);
});
}

grid
.updateConfig({ data: gridData })
.forceRender();
}

const clearGrid = (grid) => {
grid
.updateConfig({ data: [] })
.forceRender();
}

$(document).ready(function () {
$("#doMeta").on("click", function () {
doApiCall("GET", "/api/cloud-encryption-key/metadata", "#standardOutput", "#errorOutput");
});

$("#doList").on("click", function () {
clearGrid(grid);
doApiCallWithCallback("GET", "/api/site/list", (text) => {
const sites = JSON.parse(text);
const siteDict = sites.reduce((acc, site) => {
acc[site.id] = site.name;
return acc;
}, {});

doApiCallWithCallback("GET", "/api/cloud-encryption-key/list", (text) => {
const data = JSON.parse(text);
data.cloudEncryptionKeys.forEach((key) => {
key.siteName = !siteDict[key.siteId] ? "Unknown site" : siteDict[key.siteId]
});
updateGrid(grid, data);
}, errorCallback);
}, errorCallback);
});
});
</script>
</body>
</html>
File renamed without changes.
File renamed without changes.
File renamed without changes.
108 changes: 66 additions & 42 deletions webroot/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,52 +7,76 @@
<h1>UID2 Env Admin Site</h1>

<div class="notauthed" style="display: none">
<a href="/login">Login</a>
<br>
<a href="/login">Login</a>
<hr>
</div>

<div class="authed" style="display: none">
Logged in as <span id="loginEmail"></span>
<br>
<a href="/logout">Logout</a>
<br>
Logged in as <span id="loginEmail"></span>
<br>
<a href="/logout">Logout</a>
<hr>
</div>

<h3 class="ro-adm" style="display: none">On-call Support Workflow</h3>
<ul>
<li class="ro-adm" style="display: none"><a href="/adm/oncall/participant-summary.html">Participant Summary</a></li>
<li class="ro-adm" style="display: none"><a href="/adm/oncall/generate-api-key-secret.html">Generate API Key/Secret</a></li>
<li class="ro-adm" style="display: none"><a href="/adm/oncall/generate-cstg-keypair.html">Generate CSTG Keypair</a></li>
<li class="ro-adm" style="display: none"><a href="/adm/oncall/search.html">Key and Secret Search</a></li>
</ul>

<hr>

<ul>
<li class="ro-cki ro-adm" style="display: none"><a href="/adm/site.html">Site Management</a></li>
<li class="ro-adm" style="display: none"><a href="/adm/client-side-keypairs.html">Client Side Keypair Management</a></li>
</ul>

<ul>
<li class="ro-adm" style="display: none"><a href="/adm/services.html">Service Management</a></li>
<li class="ro-adm" style="display: none"><a href="/adm/service-links.html">Service Link Management</a></li>
</ul>

<ul>
<li class="ro-cki ro-adm" style="display: none"><a href="/adm/client-key.html">Client Key Management</a></li>
<li class="ro-adm" style="display: none"><a href="/adm/keysets.html">Keyset Access Management</a></li>
<li class="ro-sem" style="display: none"><a href="/adm/encryption-key.html">Encryption Key Management</a></li>
<li class="ro-sem" style="display: none"><a href="/adm/salt.html">Salts Management</a></li>
<li class="ro-opm ro-adm" style="display: none"><a href="/adm/operator-key.html">Operator Key Management</a></li>
<li class="ro-opm ro-adm" style="display: none"><a href="/adm/enclave-id.html">Enclave ID Management</a></li>
<li class="ro-opm ro-adm" style="display: none"><a href="/adm/enclave-gcp.html">GCP Enclave ID Tool</a></li>
<li class="ro-opm ro-adm" style="display: none"><a href="/adm/enclave-gcp-v2.html">GCP Enclave ID Tool(V2)</a></li>
<li class="ro-adm" style="display: none"><a href="/adm/partner-config.html">OptOut Partner Management</a></li>
<li class="ro-nil" style="display: none">No Admin Permissions</li>
</ul>
<br/>
<ul>
<li class="ro-cki ro-adm" style="display: none"><a href="/adm/key-acl.html">Deprecated: Encryption Key ACL Management</a></li>
</ul>
<div class="ro-nil" style="display: none">
<ul>
<li>No Admin Permissions</li>
</ul>
</div>

<div class="ro-adm" style="display: none">
<h3>On-call Support Workflow</h3>
<ul>
<li><a href="/adm/oncall/participant-summary.html">Participant Summary</a></li>
<li><a href="/adm/oncall/generate-api-key-secret.html">Generate API Key/Secret</a></li>
<li><a href="/adm/oncall/generate-cstg-keypair.html">Generate CSTG Keypair</a></li>
<li><a href="/adm/oncall/search.html">Key and Secret Search</a></li>
</ul>

<hr>
<h3>Clients</h3>
<ul>
<li><a href="/adm/site.html">Site Management</a></li>
<li><a href="/adm/client-key.html">Client Key Management</a></li>
<li><a href="/adm/client-side-keypair.html">Client Side Keypair Management</a></li>
</ul>

<hr>
<h3>Keysets</h3>
<ul>
<li><a href="/adm/keyset.html">Keyset Access Management</a></li>
<li><a href="/adm/encryption-key.html">UID2 Encryption Key Management</a></li>
</ul>

<hr>
<h3>Services</h3>
<ul>
<li><a href="/adm/service.html">Service Management</a></li>
<li><a href="/adm/service-link.html">Service Link Management</a></li>
</ul>

<hr>
<h3>Operators</h3>
<ul>
<li><a href="/adm/operator-key.html">Operator Key Management</a></li>
<li><a href="/adm/enclave-id.html">Enclave ID Management</a></li>
<li><a href="/adm/enclave-gcp.html">GCP Enclave ID Tool</a></li>
<li><a href="/adm/enclave-gcp-v2.html">GCP Enclave ID Tool (V2)</a></li>
<li><a href="/adm/partner-config.html">OptOut Partner Management</a></li>
</ul>

<hr>
<h3>Internal</h3>
<ul>
<li><a href="/adm/salt.html">Salt Management</a></li>
<li><a href="/adm/cloud-encryption-key.html">Cloud Encryption Key Management</a></li>
</ul>

<hr>
<h3>Deprecated</h3>
<ul>
<li><a href="/adm/key-acl.html">Deprecated: Encryption Key ACL Management</a></li>
</ul>
</div>
</body>
</html>