From 7a77b74b488889fde61337568cec37d21652c7a1 Mon Sep 17 00:00:00 2001 From: Bjørn Christian Seime Date: Thu, 23 Feb 2023 15:35:25 +0100 Subject: Revert "Revert "Store original capability (set) names from JSON config in PeerPolicy" MERGEOK" --- .../java/com/yahoo/security/tls/CapabilitySet.java | 46 ++++++++++++++++------ .../yahoo/security/tls/ConnectionAuthContext.java | 7 +++- .../com/yahoo/security/tls/PeerAuthorizer.java | 2 +- .../java/com/yahoo/security/tls/PeerPolicy.java | 24 ++++++++++- .../TransportSecurityOptionsJsonSerializer.java | 10 ++--- .../com/yahoo/security/tls/CapabilitySetTest.java | 8 ++-- 6 files changed, 72 insertions(+), 25 deletions(-) (limited to 'security-utils') diff --git a/security-utils/src/main/java/com/yahoo/security/tls/CapabilitySet.java b/security-utils/src/main/java/com/yahoo/security/tls/CapabilitySet.java index 010b8a5b228..b7cd03b49bb 100644 --- a/security-utils/src/main/java/com/yahoo/security/tls/CapabilitySet.java +++ b/security-utils/src/main/java/com/yahoo/security/tls/CapabilitySet.java @@ -1,17 +1,16 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.security.tls; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -33,10 +32,10 @@ public class CapabilitySet implements ToCapabilitySet { Capability.CONTENT__STATUS_PAGES, Capability.CONTENT__METRICS_API, Capability.CONTAINER__STATE_API, Capability.METRICSPROXY__METRICS_API, Capability.SENTINEL__CONNECTIVITY_CHECK); - private static final CapabilitySet SHARED_CAPABILITIES_APP_NODE = CapabilitySet.of( + private static final CapabilitySet SHARED_CAPABILITIES_APP_NODE = CapabilitySet.unionOf(List.of( Capability.LOGSERVER_API, Capability.CONFIGSERVER__CONFIG_API, Capability.CONFIGSERVER__FILEDISTRIBUTION_API, Capability.CONFIGPROXY__CONFIG_API, - Capability.CONFIGPROXY__FILEDISTRIBUTION_API, Capability.SLOBROK__API, TELEMETRY); + Capability.CONFIGPROXY__FILEDISTRIBUTION_API, Capability.SLOBROK__API, TELEMETRY)); public static final CapabilitySet CONTENT_NODE = predefined( "vespa.content_node", @@ -59,7 +58,7 @@ public class CapabilitySet implements ToCapabilitySet { TELEMETRY); private static CapabilitySet predefined(String name, ToCapabilitySet... capabilities) { - var instance = CapabilitySet.of(capabilities); + var instance = CapabilitySet.unionOf(List.of(capabilities)); PREDEFINED.put(name, instance); return instance; } @@ -85,14 +84,14 @@ public class CapabilitySet implements ToCapabilitySet { return new CapabilitySet(caps); } - public static CapabilitySet unionOf(Collection capSets) { + public static CapabilitySet ofSets(Collection capSets) { EnumSet union = EnumSet.noneOf(Capability.class); capSets.forEach(cs -> union.addAll(cs.caps)); return new CapabilitySet(union); } - public static CapabilitySet of(ToCapabilitySet... capabilities) { - return CapabilitySet.unionOf(Arrays.stream(capabilities).map(ToCapabilitySet::toCapabilitySet).toList()); + public static CapabilitySet unionOf(Collection caps) { + return CapabilitySet.ofSets(caps.stream().map(ToCapabilitySet::toCapabilitySet).toList()); } public static CapabilitySet of(EnumSet caps) { return new CapabilitySet(EnumSet.copyOf(caps)); } @@ -107,8 +106,33 @@ public class CapabilitySet implements ToCapabilitySet { public boolean has(Collection caps) { return this.caps.containsAll(caps); } public boolean has(Capability... caps) { return this.caps.containsAll(List.of(caps)); } - public SortedSet toNames() { - return caps.stream().map(Capability::asString).collect(Collectors.toCollection(TreeSet::new)); + public Set toCapabilityNames() { + return caps.stream().map(Capability::asString).collect(Collectors.toSet()); + } + + /** return name of the capability set if predefined, otherwise names of the individual capabilities */ + public Set resolveNames() { + var predefinedName = toPredefinedName().orElse(null); + if (predefinedName != null) return Set.of(predefinedName); + return toCapabilityNames(); + } + + /** @return the name if this is a predefined capability set, or empty if not */ + public Optional toPredefinedName() { + return PREDEFINED.entrySet().stream() + .filter(e -> e.getValue().equals(this)) + .map(Map.Entry::getKey) + .findFirst(); + } + + public static Set resolveNames(Collection capabilities) { + var names = new HashSet(); + for (ToCapabilitySet tcs : capabilities) { + if (tcs instanceof Capability c) names.add(c.asString()); + else if (tcs instanceof CapabilitySet cs) names.addAll(cs.resolveNames()); + else throw new IllegalArgumentException(tcs.toString()); + } + return Set.copyOf(names); } public Set asSet() { return Collections.unmodifiableSet(caps); } diff --git a/security-utils/src/main/java/com/yahoo/security/tls/ConnectionAuthContext.java b/security-utils/src/main/java/com/yahoo/security/tls/ConnectionAuthContext.java index d7ea93955af..9252b5619f9 100644 --- a/security-utils/src/main/java/com/yahoo/security/tls/ConnectionAuthContext.java +++ b/security-utils/src/main/java/com/yahoo/security/tls/ConnectionAuthContext.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.logging.Logger; +import java.util.stream.Collectors; import static com.yahoo.security.SubjectAlternativeName.Type.DNS; import static com.yahoo.security.SubjectAlternativeName.Type.URI; @@ -78,10 +79,14 @@ public record ConnectionAuthContext(List peerCertificateChain, b.append(". Peer "); if (peer != null) b.append("'").append(peer).append("' "); return b.append("with ").append(peerCertificateString().orElse("")).append(". Requires capabilities ") - .append(required.toNames()).append(" but peer has ").append(capabilities.toNames()) + .append(toCapabilityNames(required)).append(" but peer has ").append(toCapabilityNames(capabilities)) .append(".").toString(); } + private static String toCapabilityNames(CapabilitySet capabilities) { + return capabilities.toCapabilityNames().stream().sorted().collect(Collectors.joining(", ", "[", "]")); + } + public Optional peerCertificate() { return peerCertificateChain.isEmpty() ? Optional.empty() : Optional.of(peerCertificateChain.get(0)); } diff --git a/security-utils/src/main/java/com/yahoo/security/tls/PeerAuthorizer.java b/security-utils/src/main/java/com/yahoo/security/tls/PeerAuthorizer.java index 746fce0e290..d0e1a33fcac 100644 --- a/security-utils/src/main/java/com/yahoo/security/tls/PeerAuthorizer.java +++ b/security-utils/src/main/java/com/yahoo/security/tls/PeerAuthorizer.java @@ -49,7 +49,7 @@ public class PeerAuthorizer { // TODO Pass this through constructor CapabilityMode capabilityMode = TransportSecurityUtils.getCapabilityMode(); return new ConnectionAuthContext( - certChain, CapabilitySet.unionOf(grantedCapabilities), matchedPolicies, capabilityMode); + certChain, CapabilitySet.ofSets(grantedCapabilities), matchedPolicies, capabilityMode); } private static boolean matchesPolicy(PeerPolicy peerPolicy, String cn, List sans) { diff --git a/security-utils/src/main/java/com/yahoo/security/tls/PeerPolicy.java b/security-utils/src/main/java/com/yahoo/security/tls/PeerPolicy.java index ea3d4cfe002..f713bcb0b08 100644 --- a/security-utils/src/main/java/com/yahoo/security/tls/PeerPolicy.java +++ b/security-utils/src/main/java/com/yahoo/security/tls/PeerPolicy.java @@ -1,17 +1,25 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.security.tls; +import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.Set; /** * @author bjorncs */ -public record PeerPolicy(String policyName, Optional description, CapabilitySet capabilities, - List requiredCredentials) { +public record PeerPolicy(String policyName, Optional description, Set capabilityNames, + CapabilitySet capabilities, List requiredCredentials) { public PeerPolicy { requiredCredentials = List.copyOf(requiredCredentials); + capabilityNames = Set.copyOf(capabilityNames); + } + + public PeerPolicy(String policyName, Optional description, + CapabilitySet capabilities, List requiredCredentials) { + this(policyName, description, capabilities.resolveNames(), capabilities, requiredCredentials); } public PeerPolicy(String policyName, List requiredCredentials) { @@ -21,4 +29,16 @@ public record PeerPolicy(String policyName, Optional description, Capabi public PeerPolicy(String policyName, String description, List requiredCredentials) { this(policyName, Optional.ofNullable(description), CapabilitySet.all(), requiredCredentials); } + + public PeerPolicy(String policyName, Optional description, Collection capabilities, + List requiredCredentials) { + this(policyName, description, CapabilitySet.resolveNames(capabilities), + CapabilitySet.unionOf(capabilities), requiredCredentials); + } + + public PeerPolicy(String policyName, Optional description, Set capabilities, + List requiredCredentials) { + this(policyName, description, capabilities, CapabilitySet.fromNames(capabilities), + requiredCredentials); + } } diff --git a/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityOptionsJsonSerializer.java b/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityOptionsJsonSerializer.java index 34626e23e7a..66b90b32f79 100644 --- a/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityOptionsJsonSerializer.java +++ b/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityOptionsJsonSerializer.java @@ -96,15 +96,15 @@ class TransportSecurityOptionsJsonSerializer { throw missingFieldException("required-credentials"); } return new PeerPolicy(authorizedPeer.name, Optional.ofNullable(authorizedPeer.description), - toCapabilities(authorizedPeer.capabilities), toRequestPeerCredentials(authorizedPeer.requiredCredentials)); + toCapabilities(authorizedPeer.capabilities), toRequestPeerCredentials(authorizedPeer.requiredCredentials)); } - private static CapabilitySet toCapabilities(List capabilities) { - if (capabilities == null) return CapabilitySet.all(); + private static Set toCapabilities(List capabilities) { + if (capabilities == null) return Set.of(CapabilitySet.ALL.toPredefinedName().get()); if (capabilities.isEmpty()) throw new IllegalArgumentException("\"capabilities\" array must either be not present " + "(implies all capabilities) or contain at least one capability name"); - return CapabilitySet.fromNames(capabilities); + return Set.copyOf(capabilities); } private static List toRequestPeerCredentials(List requiredCredentials) { @@ -148,7 +148,7 @@ class TransportSecurityOptionsJsonSerializer { authorizedPeer.description = peerPolicy.description().orElse(null); CapabilitySet caps = peerPolicy.capabilities(); if (!caps.hasAll()) { - authorizedPeer.capabilities = List.copyOf(caps.toNames()); + authorizedPeer.capabilities = peerPolicy.capabilityNames().stream().sorted().toList(); } for (RequiredPeerCredential requiredPeerCredential : peerPolicy.requiredCredentials()) { RequiredCredential requiredCredential = new RequiredCredential(); diff --git a/security-utils/src/test/java/com/yahoo/security/tls/CapabilitySetTest.java b/security-utils/src/test/java/com/yahoo/security/tls/CapabilitySetTest.java index 87b16dbff1f..3fa75df27e1 100644 --- a/security-utils/src/test/java/com/yahoo/security/tls/CapabilitySetTest.java +++ b/security-utils/src/test/java/com/yahoo/security/tls/CapabilitySetTest.java @@ -4,8 +4,6 @@ package com.yahoo.security.tls; import org.junit.jupiter.api.Test; import java.util.Arrays; -import java.util.SortedSet; -import java.util.TreeSet; import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -17,10 +15,10 @@ class CapabilitySetTest { @Test void contains_all_capabilities() { - SortedSet expectedNames = Arrays.stream(Capability.values()) + var expectedNames = Arrays.stream(Capability.values()) .map(Capability::asString) - .collect(Collectors.toCollection(TreeSet::new)); - SortedSet actualNames = CapabilitySet.all().toNames(); + .collect(Collectors.toSet()); + var actualNames = CapabilitySet.all().toCapabilityNames(); assertEquals(expectedNames, actualNames); } -- cgit v1.2.3