diff options
author | Morten Tokle <mortent@verizonmedia.com> | 2021-06-14 10:42:44 +0200 |
---|---|---|
committer | Morten Tokle <mortent@verizonmedia.com> | 2021-06-14 10:42:44 +0200 |
commit | f3aa283ed994d663e7d363267eb8c2713412c618 (patch) | |
tree | 154f46752d092f85e8375969f317ca9e11e8a906 /controller-server | |
parent | d796e0be74c7f340883e923bb284228a6842c2c1 (diff) |
Include expired grants as history
Diffstat (limited to 'controller-server')
4 files changed, 75 insertions, 40 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java index 3736d18a01c..a90f10401aa 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/CuratorDb.java @@ -632,7 +632,7 @@ public class CuratorDb { /** Take lock before reading before writing */ public void writeSupportAccess(DeploymentId deploymentId, SupportAccess supportAccess) { - curator.set(supportAccessPath(deploymentId), asJson(SupportAccessSerializer.toSlime(supportAccess, true, Optional.empty()))); + curator.set(supportAccessPath(deploymentId), asJson(SupportAccessSerializer.toSlime(supportAccess))); } // -------------- Paths --------------------------------------------------- diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializer.java index 74e2bfbb471..33596fce2bd 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializer.java @@ -45,33 +45,65 @@ public class SupportAccessSerializer { private static final String certificateFieldName = "certificate"; - public static Slime toSlime(SupportAccess supportAccess, boolean includeCertificates, Optional<Instant> withCurrentState) { + public static Slime toSlime(SupportAccess supportAccess) { Slime slime = new Slime(); Cursor root = slime.setObject(); - withCurrentState.ifPresent(now -> { - Cursor status = root.setObject(stateFieldName); - SupportAccess.CurrentStatus currentState = supportAccess.currentStatus(now); - status.setString(supportAccessFieldName, currentState.state().name()); - if (currentState.state() == SupportAccess.State.ALLOWED) { - status.setString(untilFieldName, serializeInstant(currentState.allowedUntil().orElseThrow())); - status.setString(byFieldName, currentState.allowedBy().orElseThrow()); - } - } - ); - - Cursor history = root.setArray(historyFieldName); - for (SupportAccessChange change : supportAccess.changeHistory()) { - Cursor historyObject = history.addObject(); + serializeHistoricEvents(root, supportAccess.changeHistory(), List.of()); + serializeGrants(root, supportAccess.grantHistory(), true); + + return slime; + } + + public static Slime serializeCurrentState(SupportAccess supportAccess, Instant currentTime) { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + + Cursor status = root.setObject(stateFieldName); + SupportAccess.CurrentStatus currentState = supportAccess.currentStatus(currentTime); + status.setString(supportAccessFieldName, currentState.state().name()); + if (currentState.state() == SupportAccess.State.ALLOWED) { + status.setString(untilFieldName, serializeInstant(currentState.allowedUntil().orElseThrow())); + status.setString(byFieldName, currentState.allowedBy().orElseThrow()); + } + + List<SupportAccessGrant> inactiveGrants = supportAccess.grantHistory().stream() + .filter(grant -> currentTime.isAfter(grant.certificate().getNotAfter().toInstant())) + .collect(Collectors.toList()); + + serializeHistoricEvents(root, supportAccess.changeHistory(), inactiveGrants); + + // Active grants should show up in the grant section + List<SupportAccessGrant> activeGrants = supportAccess.grantHistory().stream() + .filter(grant -> currentTime.isBefore(grant.certificate().getNotAfter().toInstant())) + .collect(Collectors.toList()); + serializeGrants(root, activeGrants, false); + return slime; + } + + private static void serializeHistoricEvents(Cursor root, List<SupportAccessChange> changeEvents, List<SupportAccessGrant> historicGrants) { + Cursor historyRoot = root.setArray(historyFieldName); + for (SupportAccessChange change : changeEvents) { + Cursor historyObject = historyRoot.addObject(); historyObject.setString(stateFieldName, change.accessAllowedUntil().isPresent() ? allowedStateName : disallowedStateName); historyObject.setString(atFieldName, serializeInstant(change.changeTime())); change.accessAllowedUntil().ifPresent(allowedUntil -> historyObject.setString(untilFieldName, serializeInstant(allowedUntil))); historyObject.setString(byFieldName, change.madeBy()); } - Cursor grants = root.setArray(grantFieldName); - for (SupportAccessGrant grant : supportAccess.grantHistory()) { - Cursor grantObject = grants.addObject(); + for (SupportAccessGrant grant : historicGrants) { + Cursor historyObject = historyRoot.addObject(); + historyObject.setString(stateFieldName, "grant"); + historyObject.setString(atFieldName, serializeInstant(grant.certificate().getNotBefore().toInstant())); + historyObject.setString(untilFieldName, serializeInstant(grant.certificate().getNotAfter().toInstant())); + historyObject.setString(byFieldName, grant.requestor()); + } + } + + private static void serializeGrants(Cursor root, List<SupportAccessGrant> grants, boolean includeCertificates) { + Cursor grantsRoot = root.setArray(grantFieldName); + for (SupportAccessGrant grant : grants) { + Cursor grantObject = grantsRoot.addObject(); grantObject.setString(requestorFieldName, grant.requestor()); if (includeCertificates) { grantObject.setString(certificateFieldName, X509CertificateUtils.toPem(grant.certificate())); @@ -80,7 +112,6 @@ public class SupportAccessSerializer { grantObject.setString(notAfterFieldName, serializeInstant(grant.certificate().getNotAfter().toInstant())); } - return slime; } private static String serializeInstant(Instant i) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java index d1cbe8e14b4..017da94facc 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java @@ -31,7 +31,6 @@ import com.yahoo.restapi.Path; import com.yahoo.restapi.ResourceResponse; import com.yahoo.restapi.SlimeJsonResponse; import com.yahoo.security.KeyUtils; -import com.yahoo.security.X509CertificateUtils; import com.yahoo.slime.Cursor; import com.yahoo.slime.Inspector; import com.yahoo.slime.JsonParseException; @@ -123,7 +122,6 @@ import java.net.URISyntaxException; import java.security.DigestInputStream; import java.security.Principal; import java.security.PublicKey; -import java.security.cert.X509Certificate; import java.time.DayOfWeek; import java.time.Duration; import java.time.Instant; @@ -982,7 +980,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { private HttpResponse supportAccess(String tenantName, String applicationName, String instanceName, String environment, String region, Map<String, String> queryParameters) { DeploymentId deployment = new DeploymentId(ApplicationId.from(tenantName, applicationName, instanceName), requireZone(environment, region)); SupportAccess supportAccess = controller.supportAccess().forDeployment(deployment); - return new SlimeJsonResponse(SupportAccessSerializer.toSlime(supportAccess, false, Optional.of(controller.clock().instant()))); + return new SlimeJsonResponse(SupportAccessSerializer.serializeCurrentState(supportAccess, controller.clock().instant())); } // TODO support access: only let tenants (not operators!) allow access @@ -992,14 +990,14 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { Principal principal = requireUserPrincipal(request); Instant now = controller.clock().instant(); SupportAccess allowed = controller.supportAccess().allow(deployment, now.plus(7, ChronoUnit.DAYS), principal.getName()); - return new SlimeJsonResponse(SupportAccessSerializer.toSlime(allowed, false, Optional.of(now))); + return new SlimeJsonResponse(SupportAccessSerializer.serializeCurrentState(allowed, now)); } private HttpResponse disallowSupportAccess(String tenantName, String applicationName, String instanceName, String environment, String region, HttpRequest request) { DeploymentId deployment = new DeploymentId(ApplicationId.from(tenantName, applicationName, instanceName), requireZone(environment, region)); Principal principal = requireUserPrincipal(request); SupportAccess disallowed = controller.supportAccess().disallow(deployment, principal.getName()); - return new SlimeJsonResponse(SupportAccessSerializer.toSlime(disallowed, false, Optional.of(controller.clock().instant()))); + return new SlimeJsonResponse(SupportAccessSerializer.serializeCurrentState(disallowed, controller.clock().instant())); } private HttpResponse metrics(String tenantName, String applicationName, String instanceName, String environment, String region) { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializerTest.java index 2a43f8cc4f3..97cf53d7b89 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/SupportAccessSerializerTest.java @@ -7,7 +7,7 @@ import com.yahoo.security.SignatureAlgorithm; import com.yahoo.security.X509CertificateBuilder; import com.yahoo.security.X509CertificateUtils; import com.yahoo.slime.JsonFormat; -import com.yahoo.slime.SlimeUtils; +import com.yahoo.slime.Slime; import com.yahoo.vespa.hosted.controller.support.access.SupportAccess; import com.yahoo.vespa.hosted.controller.support.access.SupportAccessGrant; import org.intellij.lang.annotations.Language; @@ -21,7 +21,6 @@ import java.math.BigInteger; import java.security.cert.X509Certificate; import java.time.Instant; import java.time.temporal.ChronoUnit; -import java.util.Optional; import static org.junit.Assert.*; @@ -80,7 +79,8 @@ public class SupportAccessSerializerTest { @Test public void serialize_default() { - assertSerialized(SupportAccess.DISALLOWED_NO_HISTORY, true, Instant.EPOCH, "{\n" + + var slime = SupportAccessSerializer.serializeCurrentState(SupportAccess.DISALLOWED_NO_HISTORY, Instant.EPOCH); + assertSerialized(slime, "{\n" + " \"state\": {\n" + " \"supportAccess\": \"NOT_ALLOWED\"\n" + " },\n" + @@ -93,12 +93,14 @@ public class SupportAccessSerializerTest { @Test public void serialize_with_certificates() { - assertSerialized(supportAccessExample, true, null, expectedWithCertificates); + var slime = SupportAccessSerializer.toSlime(supportAccessExample); + assertSerialized(slime, expectedWithCertificates); } @Test public void serialize_with_status() { - assertSerialized(supportAccessExample, false, hour(32), + var slime = SupportAccessSerializer.serializeCurrentState(supportAccessExample, hour(12)); + assertSerialized(slime, "{\n" + " \"state\": {\n" + " \"supportAccess\": \"ALLOWED\",\n" + @@ -122,6 +124,12 @@ public class SupportAccessSerializerTest { " \"at\": \"1970-01-01T02:00:00Z\",\n" + " \"until\": \"1970-01-02T00:00:00Z\",\n" + " \"by\": \"andreer\"\n" + + " },\n" + + " {\n" + + " \"state\": \"grant\",\n" + + " \"at\": \"1970-01-01T03:00:00Z\",\n" + + " \"until\": \"1970-01-01T04:00:00Z\",\n" + + " \"by\": \"mortent\"\n" + " }\n" + " ],\n" + " \"grants\": [\n" + @@ -129,28 +137,26 @@ public class SupportAccessSerializerTest { " \"requestor\": \"mortent\",\n" + " \"notBefore\": \"1970-01-01T07:00:00Z\",\n" + " \"notAfter\": \"1970-01-01T19:00:00Z\"\n" + - " },\n" + - " {\n" + - " \"requestor\": \"mortent\",\n" + - " \"notBefore\": \"1970-01-01T03:00:00Z\",\n" + - " \"notAfter\": \"1970-01-01T04:00:00Z\"\n" + - " }\n" + + " }" + + "\n" + " ]\n" + "}\n"); } @Test public void deserialize() { - assertEquals(supportAccessExample, SupportAccessSerializer.fromSlime(SlimeUtils.jsonToSlime(expectedWithCertificates))); + var slime = SupportAccessSerializer.toSlime(supportAccessExample); + assertSerialized(slime, expectedWithCertificates); + + var deserialized = SupportAccessSerializer.fromSlime(slime); + assertEquals(supportAccessExample, deserialized); } private Instant hour(long h) { return Instant.EPOCH.plus(h, ChronoUnit.HOURS); } - private void assertSerialized(SupportAccess supportAccess, boolean includeCertificates, Instant now, String expected) { - var slime = SupportAccessSerializer.toSlime(supportAccess, includeCertificates, Optional.ofNullable(now)); - + private void assertSerialized(Slime slime, String expected) { try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { new JsonFormat(false).encode(out, slime); assertEquals(expected, out.toString()); |