Skip to content

Commit d283230

Browse files
committed
Add the ability to delete client-side keypairs
1 parent cc74967 commit d283230

File tree

4 files changed

+133
-11
lines changed

4 files changed

+133
-11
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public enum Endpoints {
2222

2323
API_CLIENT_SIDE_KEYPAIRS_ADD("/api/client_side_keypairs/add"),
2424
API_CLIENT_SIDE_KEYPAIRS_UPDATE("/api/client_side_keypairs/update"),
25+
API_CLIENT_SIDE_KEYPAIRS_DELETE("/api/client_side_keypairs/delete"),
2526
API_CLIENT_SIDE_KEYPAIRS_LIST("/api/client_side_keypairs/list"),
2627
API_CLIENT_SIDE_KEYPAIRS_SUBSCRIPTIONID("/api/client_side_keypairs/:subscriptionId"),
2728
API_CLIENT_SIDE_KEYPAIRS_BY_SITE("/api/v2/sites/:siteId/client-side-keypairs"),

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

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ public void setupRoutes(Router router) {
7676
this.handleUpdateKeypair(ctx);
7777
}
7878
}, new AuditParams(Collections.emptyList(), List.of("subscription_id", "name", "contact", "disabled")), Role.MAINTAINER, Role.SHARING_PORTAL));
79+
router.post(API_CLIENT_SIDE_KEYPAIRS_DELETE.toString()).blockingHandler(auth.handle((ctx) -> {
80+
synchronized (writeLock) {
81+
this.handleDeleteKeypair(ctx);
82+
}
83+
}, new AuditParams(Collections.emptyList(), List.of("subscription_id")), Role.PRIVILEGED, Role.SHARING_PORTAL));
7984
router.get(API_CLIENT_SIDE_KEYPAIRS_LIST.toString()).handler(
8085
auth.handle(this::handleListAllKeypairs, Role.MAINTAINER, Role.METRICS_EXPORT));
8186
router.get(API_CLIENT_SIDE_KEYPAIRS_SUBSCRIPTIONID.toString()).handler(
@@ -119,22 +124,16 @@ private void handleAddKeypair(RoutingContext rc) {
119124
}
120125

121126
private void handleUpdateKeypair(RoutingContext rc) {
122-
final JsonObject body = rc.body().asJsonObject();
123-
final String subscriptionId = body.getString("subscription_id");
127+
JsonObject body = getRequestBody(rc);
128+
if (body == null) return;
129+
124130
String contact = body.getString("contact");
125131
Boolean disabled = body.getBoolean("disabled");
126132
String name = body.getString("name");
127133

128-
if (subscriptionId == null) {
129-
ResponseUtil.error(rc, 400, "Required parameters: subscription_id");
130-
return;
131-
}
134+
ClientSideKeypair keypair = validateAndGetKeypair(rc, body);
135+
if (keypair == null) return;
132136

133-
ClientSideKeypair keypair = this.keypairStore.getSnapshot().getKeypair(subscriptionId);
134-
if (keypair == null) {
135-
ResponseUtil.error(rc, 404, "Failed to find a keypair for subscription id: " + subscriptionId);
136-
return;
137-
}
138137

139138
if (contact == null && disabled == null && name == null) {
140139
ResponseUtil.error(rc, 400, "Updatable parameters: contact, disabled, name");
@@ -179,6 +178,30 @@ private void handleUpdateKeypair(RoutingContext rc) {
179178
.end(json.encode());
180179
}
181180

181+
private void handleDeleteKeypair(RoutingContext rc) {
182+
JsonObject body = getRequestBody(rc);
183+
if (body == null) return;
184+
185+
ClientSideKeypair keypair = validateAndGetKeypair(rc, body);
186+
if (keypair == null) return;
187+
188+
Set<ClientSideKeypair> allKeypairs = new HashSet<>(this.keypairStore.getAll());
189+
allKeypairs.remove(keypair);
190+
191+
try {
192+
storeWriter.upload(allKeypairs, null);
193+
} catch (Exception e) {
194+
ResponseUtil.errorInternal(rc, "failed to upload keypairs", e);
195+
return;
196+
}
197+
198+
JsonObject responseJson = new JsonObject()
199+
.put("success", true)
200+
.put("deleted_keypair", createKeypairJsonObject(toJsonWithoutPrivateKey(keypair)));
201+
rc.response().putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
202+
.end(responseJson.encode());
203+
}
204+
182205
public Iterable<ClientSideKeypair> getKeypairsBySite(int siteId) {
183206
return this.keypairStore.getSnapshot().getSiteKeypairs(siteId);
184207
}
@@ -243,4 +266,27 @@ public ClientSideKeypair createAndSaveSiteKeypair(int siteId, String contact, bo
243266
return newKeypair;
244267

245268
}
269+
270+
private JsonObject getRequestBody(RoutingContext rc) {
271+
JsonObject body = rc.body() != null ? rc.body().asJsonObject() : null;
272+
if (body == null) {
273+
ResponseUtil.error(rc, 400, "json payload required but not provided");
274+
}
275+
return body;
276+
}
277+
278+
private ClientSideKeypair validateAndGetKeypair(RoutingContext rc, JsonObject body) {
279+
String subscriptionId = body.getString("subscription_id");
280+
if (subscriptionId == null) {
281+
ResponseUtil.error(rc, 400, "Required parameters: subscription_id");
282+
return null;
283+
}
284+
285+
ClientSideKeypair keypair = this.keypairStore.getSnapshot().getKeypair(subscriptionId);
286+
if (keypair == null) {
287+
ResponseUtil.error(rc, 404, "Failed to find a keypair for subscription id: " + subscriptionId);
288+
return null;
289+
}
290+
return keypair;
291+
}
246292
}

src/test/java/com/uid2/admin/vertx/ClientSideKeypairServiceTest.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,66 @@ void updateKeypairDisabledAndName(Vertx vertx, VertxTestContext testContext) thr
556556
testContext.completeNow();
557557
});
558558
}
559+
560+
@Test
561+
void deleteKeypairNoSubscriptionId(Vertx vertx, VertxTestContext testContext) throws Exception {
562+
fakeAuth(Role.PRIVILEGED);
563+
564+
setKeypairs(new ArrayList<>());
565+
566+
JsonObject jo = new JsonObject();
567+
568+
post(vertx, testContext, "api/client_side_keypairs/delete", jo.encode(), response -> {
569+
assertEquals(400, response.statusCode());
570+
assertEquals("Required parameters: subscription_id", response.bodyAsJsonObject().getString("message"));
571+
verify(keypairStoreWriter, times(0)).upload(any(), isNull());
572+
testContext.completeNow();
573+
});
574+
}
575+
576+
@Test
577+
void deleteKeypairBadSubscriptionId(Vertx vertx, VertxTestContext testContext) throws Exception {
578+
fakeAuth(Role.PRIVILEGED);
579+
580+
Map<String, ClientSideKeypair> keypairs = new HashMap<>() {{
581+
put("89aZ234567", new ClientSideKeypair("89aZ234567", pub1, priv1, 124, "[email protected]", Instant.now(), false, name1));
582+
put("9aZ2345678", new ClientSideKeypair("9aZ2345678", pub2, priv2, 125, "[email protected]", Instant.now(), false, name2));
583+
}};
584+
setKeypairs(new ArrayList<>(keypairs.values()));
585+
586+
JsonObject jo = new JsonObject();
587+
jo.put("subscription_id", "bad-id");
588+
589+
post(vertx, testContext, "api/client_side_keypairs/delete", jo.encode(), response -> {
590+
assertEquals(404, response.statusCode());
591+
assertEquals("Failed to find a keypair for subscription id: bad-id", response.bodyAsJsonObject().getString("message"));
592+
verify(keypairStoreWriter, times(0)).upload(any(), isNull());
593+
testContext.completeNow();
594+
});
595+
}
596+
597+
@Test
598+
void deleteKeypair(Vertx vertx, VertxTestContext testContext) throws Exception {
599+
fakeAuth(Role.PRIVILEGED);
600+
601+
Instant time = Instant.now();
602+
ClientSideKeypair keypairToDelete = new ClientSideKeypair("89aZ234567", pub1, priv1, 124, "[email protected]", time, false, name1);
603+
ClientSideKeypair remainingKeypair = new ClientSideKeypair("9aZ2345678", pub2, priv2, 124, "[email protected]", time, false, name2);
604+
605+
setKeypairs(List.of(keypairToDelete, remainingKeypair));
606+
setSites(new Site(124, "test", true));
607+
608+
JsonObject jo = new JsonObject();
609+
jo.put("subscription_id", "89aZ234567");
610+
611+
post(vertx, testContext, "api/client_side_keypairs/delete", jo.encode(), response -> {
612+
assertEquals(200, response.statusCode());
613+
assertEquals(true, response.bodyAsJsonObject().getBoolean("success"));
614+
validateKeypair(keypairToDelete, "test", response.bodyAsJsonObject().getJsonObject("deleted_keypair"));
615+
verify(keypairStoreWriter, times(1)).upload(collectionOfSize(1), isNull());
616+
testContext.completeNow();
617+
});
618+
}
559619
}
560620

561621

webroot/adm/client-side-keypair.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ <h3>Operations</h3>
3333
<li class="ro-sem" style="display: none"><a href="#" id="doListSubscription">Reveal Keypair By Subscription Id</a></li>
3434
<li class="ro-sem" style="display: none"><a href="#" id="doCreate">Create Keypair</a></li>
3535
<li class="ro-sem" style="display: none"><a href="#" id="doUpdate">Update Keypair</a></li>
36+
<li class="ro-sem" style="display: none"><a href="#" id="doDelete">Delete Keypair</a></li>
3637
</ul>
3738

3839
<br>
@@ -109,6 +110,20 @@ <h3>Output</h3>
109110
doApiCall('POST', '/api/client_side_keypairs/update', '#standardOutput', '#errorOutput', JSON.stringify(payload));
110111
});
111112

113+
$('#doDelete').on('click', function () {
114+
const subscriptionId = $('#subscriptionId').val();
115+
if (!subscriptionId) {
116+
$('#errorOutput').text("required parameters: subscription_id");
117+
return;
118+
}
119+
120+
if (!confirm(`Are you sure you want to delete ${subscriptionId}?`)) return;
121+
122+
const payload = {"subscription_id": subscriptionId};
123+
124+
doApiCall('POST', '/api/client_side_keypairs/delete', '#standardOutput', '#errorOutput', JSON.stringify(payload));
125+
});
126+
112127
});
113128

114129
function getUpdateKeypairConfirmationMessage(disabled, subscriptionId) {

0 commit comments

Comments
 (0)