Skip to content

Commit f83f975

Browse files
authored
Merge pull request #444 from IABTechLab/gdm-UID2-5196-cloud-encryption-list
Added new cloud encryption key page
2 parents c5d09db + c56f295 commit f83f975

File tree

11 files changed

+261
-59
lines changed

11 files changed

+261
-59
lines changed

src/main/java/com/uid2/admin/cloudencryption/CloudEncryptionKeyManager.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.uid2.shared.auth.RotatingOperatorKeyProvider;
77
import com.uid2.shared.model.CloudEncryptionKey;
88
import com.uid2.shared.store.reader.RotatingCloudEncryptionKeyProvider;
9+
import io.vertx.core.json.JsonObject;
910
import org.slf4j.Logger;
1011
import org.slf4j.LoggerFactory;
1112

@@ -34,6 +35,15 @@ public CloudEncryptionKeyManager(
3435
this.planner = planner;
3536
}
3637

38+
public JsonObject getMetadata() throws Exception {
39+
return keyProvider.getMetadata();
40+
}
41+
42+
public Set<CloudEncryptionKeySummary> getKeySummaries() throws Exception {
43+
refreshCloudData();
44+
return existingKeys.stream().map(CloudEncryptionKeySummary::fromFullKey).collect(Collectors.toSet());
45+
}
46+
3747
// For any site that has an operator create a new key activating in one hour
3848
// Keep up to 10 most recent old keys per site, delete the rest
3949
public void rotateKeys(boolean shouldFail) throws Exception {
@@ -66,11 +76,6 @@ public void backfillKeys() throws Exception {
6676
}
6777
}
6878

69-
public Set<CloudEncryptionKeySummary> getKeySummaries() throws Exception {
70-
refreshCloudData();
71-
return existingKeys.stream().map(CloudEncryptionKeySummary::fromFullKey).collect(Collectors.toSet());
72-
}
73-
7479
private void writeKeys(Set<CloudEncryptionKey> desiredKeys) throws Exception {
7580
var keysForWriting = desiredKeys.stream().collect(Collectors.toMap(
7681
CloudEncryptionKey::getId,
@@ -85,4 +90,4 @@ private void refreshCloudData() throws Exception {
8590
operatorKeys = new HashSet<>(operatorKeyProvider.getAll());
8691
existingKeys = new HashSet<>(keyProvider.getAll().values());
8792
}
88-
}
93+
}

src/main/java/com/uid2/admin/vertx/Endpoints.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ public enum Endpoints {
9898
API_SITE_APP_NAMES("/api/site/app_names"),
9999
API_SITE_UPDATE("/api/site/update"),
100100

101+
CLOUD_ENCRYPTION_KEY_METADATA("/api/cloud-encryption-key/metadata"),
101102
CLOUD_ENCRYPTION_KEY_LIST("/api/cloud-encryption-key/list"),
102103
CLOUD_ENCRYPTION_KEY_ROTATE("/api/cloud-encryption-key/rotate"),
103104

src/main/java/com/uid2/admin/vertx/service/CloudEncryptionKeyService.java

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ public CloudEncryptionKeyService(
3131

3232
@Override
3333
public void setupRoutes(Router router) {
34+
router.get(Endpoints.CLOUD_ENCRYPTION_KEY_METADATA.toString()).handler(
35+
auth.handle(this::handleMetadata, Role.MAINTAINER));
36+
3437
router.get(Endpoints.CLOUD_ENCRYPTION_KEY_LIST.toString()).handler(
3538
auth.handle(this::handleList, Role.MAINTAINER)
3639
);
@@ -40,6 +43,25 @@ public void setupRoutes(Router router) {
4043
);
4144
}
4245

46+
private void handleMetadata(RoutingContext rc) {
47+
try {
48+
rc.response()
49+
.putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
50+
.end(keyManager.getMetadata().encode());
51+
} catch (Exception e) {
52+
rc.fail(500, e);
53+
}
54+
}
55+
56+
private void handleList(RoutingContext rc) {
57+
try {
58+
var response = new CloudEncryptionKeyListResponse(keyManager.getKeySummaries());
59+
respondWithJson(rc, response);
60+
} catch (Exception e) {
61+
rc.fail(500, e);
62+
}
63+
}
64+
4365
private void handleRotate(RoutingContext rc) {
4466
try {
4567
var shouldFail = !rc.queryParam("fail").isEmpty();
@@ -59,15 +81,6 @@ private void handleRotate(RoutingContext rc) {
5981
}
6082
}
6183

62-
private void handleList(RoutingContext rc) {
63-
try {
64-
var response = new CloudEncryptionKeyListResponse(keyManager.getKeySummaries());
65-
respondWithJson(rc, response);
66-
} catch (Exception e) {
67-
rc.fail(500, e);
68-
}
69-
}
70-
7184
private static void respondWithJson(RoutingContext rc, CloudEncryptionKeyListResponse response) throws JsonProcessingException {
7285
rc.response()
7386
.putHeader(HttpHeaders.CONTENT_TYPE, "application/json")

src/main/java/com/uid2/admin/vertx/service/EncryptedFilesSyncService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public EncryptedFilesSyncService(
3333
this.jobDispatcher = jobDispatcher;
3434
this.writeLock = writeLock;
3535
this.config = config;
36-
this.cloudEncryptionKeyProvider =cloudEncryptionKeyProvider;
36+
this.cloudEncryptionKeyProvider = cloudEncryptionKeyProvider;
3737
}
3838

3939
@Override

src/test/java/com/uid2/admin/auth/OktaAuthFactoryTest.java renamed to src/test/java/com/uid2/admin/auth/OktaAuthProviderTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import io.vertx.core.Vertx;
44
import io.vertx.core.json.JsonObject;
5-
import io.vertx.ext.auth.oauth2.OAuth2Auth;
65
import io.vertx.ext.web.Route;
76
import io.vertx.ext.web.handler.AuthenticationHandler;
87
import io.vertx.ext.web.handler.impl.OAuth2AuthHandlerImpl;
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
<html>
2+
<head>
3+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
4+
<script src="/js/main.js"></script>
5+
<script src="https://unpkg.com/gridjs/dist/gridjs.umd.js"></script>
6+
<link href="https://unpkg.com/gridjs/dist/theme/mermaid.min.css" rel="stylesheet" />
7+
<style>
8+
.active { color: darkgreen; }
9+
.inactive { color: darkred; }
10+
</style>
11+
</head>
12+
<body>
13+
<h1>UID2 Env - Cloud Encryption Key Management</h1>
14+
15+
<a href="/">Back</a>
16+
17+
<br>
18+
<br>
19+
20+
<div class="ro-adm" style="display: none">
21+
<h3>Operations</h3>
22+
<ul>
23+
<li><a href="#" id="doMeta">Get Metadata</a></li>
24+
<li><a href="#" id="doList">List Cloud Encryption Keys</a></li>
25+
</ul>
26+
27+
<br>
28+
29+
<h3>Output</h3>
30+
<div id="output">
31+
<pre id="errorOutput"></pre>
32+
<pre id="standardOutput"></pre>
33+
</div>
34+
<div id="output-table"></div>
35+
</div>
36+
37+
<script language="JavaScript">
38+
const grid = new gridjs.Grid({
39+
columns: [
40+
{
41+
name: "Key ID",
42+
formatter: (cell, row) => {
43+
return gridjs.html(`<span class="${row.cells[4].data}">${cell}</span>`)
44+
}
45+
},
46+
{
47+
name: "Site",
48+
formatter: (cell, row) => {
49+
return gridjs.html(`<span class="${row.cells[4].data}">${cell.siteId} - ${cell.siteName}</span>`)
50+
}
51+
},
52+
{
53+
name: "Activates",
54+
formatter: (cell, row) => {
55+
return gridjs.html(`<span class="${row.cells[4].data}">${formatDate(cell)}</span>`)
56+
}
57+
},
58+
{
59+
name: "Created",
60+
formatter: (cell, row) => {
61+
return gridjs.html(`<span class="${row.cells[4].data}">${formatDate(cell)}</span>`)
62+
}
63+
},
64+
{
65+
name: "Style",
66+
hidden: true
67+
}
68+
],
69+
data: [],
70+
sort: true,
71+
search: {
72+
selector: (cell, rowIndex, cellIndex) => {
73+
if (cellIndex === 0) {
74+
return cell;
75+
} else if (cellIndex === 1) {
76+
return `${cell.siteId} - ${cell.siteName}`;
77+
}
78+
}
79+
},
80+
language: {
81+
search: {
82+
placeholder: "Search by Key ID or Site..."
83+
}
84+
}
85+
})
86+
.render(document.getElementById("output-table"));
87+
88+
const formatDate = (text) => {
89+
const date = new Date(text);
90+
const options = {
91+
year: "numeric",
92+
month: "long",
93+
day: "numeric",
94+
hour: "2-digit",
95+
minute: "2-digit",
96+
second: "2-digit",
97+
timeZoneName: "short"
98+
};
99+
return date.toLocaleString("en-US", options);
100+
}
101+
102+
const updateGrid = (grid, data) => {
103+
const groupedData = data.cloudEncryptionKeys.reduce((acc, key) => {
104+
if (!acc[key.siteId]) {
105+
acc[key.siteId] = [];
106+
}
107+
acc[key.siteId].push(key);
108+
return acc;
109+
}, {});
110+
111+
const gridData = [];
112+
for (const siteId in groupedData) {
113+
const keys = groupedData[siteId];
114+
const latestKey = keys.reduce((latest, key) => new Date(key.activates) > new Date(latest.activates) ? key : latest);
115+
116+
keys.forEach((key) => {
117+
let style = key === latestKey ? "active" : "inactive";
118+
119+
gridData.push([ key.id, { siteId: key.siteId, siteName: key.siteName }, key.activates, key.created, style ]);
120+
});
121+
}
122+
123+
grid
124+
.updateConfig({ data: gridData })
125+
.forceRender();
126+
}
127+
128+
const clearGrid = (grid) => {
129+
grid
130+
.updateConfig({ data: [] })
131+
.forceRender();
132+
}
133+
134+
$(document).ready(function () {
135+
$("#doMeta").on("click", function () {
136+
doApiCall("GET", "/api/cloud-encryption-key/metadata", "#standardOutput", "#errorOutput");
137+
});
138+
139+
$("#doList").on("click", function () {
140+
clearGrid(grid);
141+
doApiCallWithCallback("GET", "/api/site/list", (text) => {
142+
const sites = JSON.parse(text);
143+
const siteDict = sites.reduce((acc, site) => {
144+
acc[site.id] = site.name;
145+
return acc;
146+
}, {});
147+
148+
doApiCallWithCallback("GET", "/api/cloud-encryption-key/list", (text) => {
149+
const data = JSON.parse(text);
150+
data.cloudEncryptionKeys.forEach((key) => {
151+
key.siteName = !siteDict[key.siteId] ? "Unknown site" : siteDict[key.siteId]
152+
});
153+
updateGrid(grid, data);
154+
}, errorCallback);
155+
}, errorCallback);
156+
});
157+
});
158+
</script>
159+
</body>
160+
</html>

0 commit comments

Comments
 (0)