aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@verizonmedia.com>2022-07-19 14:30:27 +0200
committerGitHub <noreply@github.com>2022-07-19 14:30:27 +0200
commit46ba1b00aa19e937e2c257b34c23417adeef56eb (patch)
tree7e595f7ca0c17bc74b07c18472f4cd2d4f57c4d4
parent8be6dd28753425126507b68c93a24607124871eb (diff)
parent529a26d7e1062a006196366454f1a047ca31202c (diff)
Merge pull request #23496 from vespa-engine/bjorncs/capabilitiesv8.21.11
Bjorncs/capabilities
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java8
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizerTest.java9
-rw-r--r--jrt/src/com/yahoo/jrt/Connection.java6
-rw-r--r--jrt/src/com/yahoo/jrt/CryptoSocket.java6
-rw-r--r--jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java6
-rw-r--r--jrt/src/com/yahoo/jrt/SecurityContext.java24
-rw-r--r--jrt/src/com/yahoo/jrt/Target.java6
-rw-r--r--jrt/src/com/yahoo/jrt/TlsCryptoSocket.java37
-rw-r--r--jrt/tests/com/yahoo/jrt/EchoTest.java23
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/ConfigFileBasedTlsContext.java7
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java11
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityOptions.java6
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/authz/AuthorizationResult.java45
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/authz/ConnectionAuthContext.java26
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizer.java18
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManager.java44
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/json/TransportSecurityOptionsJsonSerializer.java43
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/policy/AuthorizedPeers.java6
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/policy/CapabilitySet.java15
-rw-r--r--security-utils/src/test/java/com/yahoo/security/tls/authz/PeerAuthorizerTest.java43
-rw-r--r--security-utils/src/test/java/com/yahoo/security/tls/policy/CapabilitySetTest.java2
21 files changed, 196 insertions, 195 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java
index f5b570fed40..288d064f150 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizer.java
@@ -10,9 +10,9 @@ import com.yahoo.config.provision.security.NodeIdentifier;
import com.yahoo.config.provision.security.NodeIdentifierException;
import com.yahoo.config.provision.security.NodeIdentity;
import com.yahoo.jrt.Request;
-import com.yahoo.jrt.SecurityContext;
import com.yahoo.security.tls.MixedMode;
import com.yahoo.security.tls.TransportSecurityUtils;
+import com.yahoo.security.tls.authz.ConnectionAuthContext;
import com.yahoo.vespa.config.ConfigKey;
import com.yahoo.vespa.config.protocol.JRTServerConfigRequestV3;
import com.yahoo.vespa.config.server.RequestHandler;
@@ -166,14 +166,14 @@ public class MultiTenantRpcAuthorizer implements RpcAuthorizer {
// TODO Make peer identity mandatory once TLS mixed mode is removed
private Optional<NodeIdentity> getPeerIdentity(Request request) {
- Optional<SecurityContext> securityContext = request.target().getSecurityContext();
- if (securityContext.isEmpty()) {
+ Optional<ConnectionAuthContext> authCtx = request.target().getConnectionAuthContext();
+ if (authCtx.isEmpty()) {
if (TransportSecurityUtils.getInsecureMixedMode() == MixedMode.DISABLED) {
throw new IllegalStateException("Security context missing"); // security context should always be present
}
return Optional.empty(); // client choose to communicate over insecure channel
}
- List<X509Certificate> certChain = securityContext.get().peerCertificateChain();
+ List<X509Certificate> certChain = authCtx.get().peerCertificateChain();
if (certChain.isEmpty()) {
throw new IllegalStateException("Client authentication is not enforced!"); // clients should be required to authenticate when TLS is enabled
}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizerTest.java
index 2650b23a38e..5b5b795a412 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/security/MultiTenantRpcAuthorizerTest.java
@@ -11,7 +11,6 @@ import com.yahoo.config.provision.security.NodeIdentifier;
import com.yahoo.config.provision.security.NodeIdentifierException;
import com.yahoo.config.provision.security.NodeIdentity;
import com.yahoo.jrt.Request;
-import com.yahoo.jrt.SecurityContext;
import com.yahoo.jrt.StringValue;
import com.yahoo.jrt.Target;
import com.yahoo.jrt.Values;
@@ -19,6 +18,8 @@ import com.yahoo.security.KeyAlgorithm;
import com.yahoo.security.KeyUtils;
import com.yahoo.security.SignatureAlgorithm;
import com.yahoo.security.X509CertificateBuilder;
+import com.yahoo.security.tls.authz.ConnectionAuthContext;
+import com.yahoo.security.tls.policy.CapabilitySet;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.JsonFormat;
import com.yahoo.slime.Slime;
@@ -248,10 +249,10 @@ public class MultiTenantRpcAuthorizerTest {
}
private static Request mockJrtRpcRequest(String payload) {
- SecurityContext securityContext = mock(SecurityContext.class);
- when(securityContext.peerCertificateChain()).thenReturn(PEER_CERTIFICATE_CHAIN);
+ ConnectionAuthContext authContext =
+ new ConnectionAuthContext(PEER_CERTIFICATE_CHAIN, CapabilitySet.none(), Set.of());
Target target = mock(Target.class);
- when(target.getSecurityContext()).thenReturn(Optional.of(securityContext));
+ when(target.getConnectionAuthContext()).thenReturn(Optional.of(authContext));
Request request = mock(Request.class);
when(request.target()).thenReturn(target);
Values values = new Values();
diff --git a/jrt/src/com/yahoo/jrt/Connection.java b/jrt/src/com/yahoo/jrt/Connection.java
index 00aceb7e352..644e2ef4ff3 100644
--- a/jrt/src/com/yahoo/jrt/Connection.java
+++ b/jrt/src/com/yahoo/jrt/Connection.java
@@ -1,6 +1,8 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.jrt;
+import com.yahoo.security.tls.authz.ConnectionAuthContext;
+
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
@@ -436,9 +438,9 @@ class Connection extends Target {
}
@Override
- public Optional<SecurityContext> getSecurityContext() {
+ public Optional<ConnectionAuthContext> getConnectionAuthContext() {
return Optional.ofNullable(socket)
- .flatMap(CryptoSocket::getSecurityContext);
+ .flatMap(CryptoSocket::getConnectionAuthContext);
}
public boolean isClient() {
diff --git a/jrt/src/com/yahoo/jrt/CryptoSocket.java b/jrt/src/com/yahoo/jrt/CryptoSocket.java
index 78308b76624..aac91362405 100644
--- a/jrt/src/com/yahoo/jrt/CryptoSocket.java
+++ b/jrt/src/com/yahoo/jrt/CryptoSocket.java
@@ -2,6 +2,8 @@
package com.yahoo.jrt;
+import com.yahoo.security.tls.authz.ConnectionAuthContext;
+
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
@@ -103,10 +105,10 @@ public interface CryptoSocket {
public void dropEmptyBuffers();
/**
- * Returns the security context for the current connection (given handshake completed),
+ * Returns the auth context for the current connection (given handshake completed),
* or empty if the current connection is not secure.
*/
- default public Optional<SecurityContext> getSecurityContext() {
+ default public Optional<ConnectionAuthContext> getConnectionAuthContext() {
return Optional.empty();
}
}
diff --git a/jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java b/jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java
index df01f4f2fa7..42442289cd1 100644
--- a/jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java
+++ b/jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java
@@ -1,6 +1,8 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.jrt;
+import com.yahoo.security.tls.authz.ConnectionAuthContext;
+
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
@@ -130,5 +132,7 @@ public class MaybeTlsCryptoSocket implements CryptoSocket {
@Override public int write(ByteBuffer src) throws IOException { return socket.write(src); }
@Override public FlushResult flush() throws IOException { return socket.flush(); }
@Override public void dropEmptyBuffers() { socket.dropEmptyBuffers(); }
- @Override public Optional<SecurityContext> getSecurityContext() { return Optional.ofNullable(socket).flatMap(CryptoSocket::getSecurityContext); }
+ @Override public Optional<ConnectionAuthContext> getConnectionAuthContext() {
+ return Optional.ofNullable(socket).flatMap(CryptoSocket::getConnectionAuthContext);
+ }
}
diff --git a/jrt/src/com/yahoo/jrt/SecurityContext.java b/jrt/src/com/yahoo/jrt/SecurityContext.java
deleted file mode 100644
index 4eef99cb93f..00000000000
--- a/jrt/src/com/yahoo/jrt/SecurityContext.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.jrt;
-
-import java.security.cert.X509Certificate;
-import java.util.List;
-
-/**
- * @author bjorncs
- */
-public class SecurityContext {
-
- private final List<X509Certificate> peerCertificateChain;
-
- public SecurityContext(List<X509Certificate> peerCertificateChain) {
- this.peerCertificateChain = peerCertificateChain;
- }
-
- /**
- * @return the peer certificate chain if the peer was authenticated, empty list if not.
- */
- public List<X509Certificate> peerCertificateChain() {
- return peerCertificateChain;
- }
-}
diff --git a/jrt/src/com/yahoo/jrt/Target.java b/jrt/src/com/yahoo/jrt/Target.java
index a59aa341fe0..239a71f53b3 100644
--- a/jrt/src/com/yahoo/jrt/Target.java
+++ b/jrt/src/com/yahoo/jrt/Target.java
@@ -1,6 +1,8 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.jrt;
+import com.yahoo.security.tls.authz.ConnectionAuthContext;
+
import java.util.Optional;
/**
@@ -69,9 +71,9 @@ public abstract class Target {
public Exception getConnectionLostReason() { return null; }
/**
- * Returns the security context associated with this target, or empty if no connection or is insecure.
+ * Returns the connection auth context associated with this target, or empty if no connection or is insecure.
*/
- public abstract Optional<SecurityContext> getSecurityContext();
+ public abstract Optional<ConnectionAuthContext> getConnectionAuthContext();
/**
* Check if this target represents the client side of a
diff --git a/jrt/src/com/yahoo/jrt/TlsCryptoSocket.java b/jrt/src/com/yahoo/jrt/TlsCryptoSocket.java
index a899938dd45..ecd76e1eb17 100644
--- a/jrt/src/com/yahoo/jrt/TlsCryptoSocket.java
+++ b/jrt/src/com/yahoo/jrt/TlsCryptoSocket.java
@@ -1,7 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.jrt;
-import com.yahoo.security.tls.authz.AuthorizationResult;
+import com.yahoo.security.tls.authz.ConnectionAuthContext;
import com.yahoo.security.tls.authz.PeerAuthorizerTrustManager;
import javax.net.ssl.SSLEngine;
@@ -9,19 +9,14 @@ import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SocketChannel;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.List;
import java.util.Optional;
import java.util.logging.Logger;
-import static java.util.stream.Collectors.toList;
import static javax.net.ssl.SSLEngineResult.Status;
/**
@@ -46,7 +41,7 @@ public class TlsCryptoSocket implements CryptoSocket {
private int sessionApplicationBufferSize;
private ByteBuffer handshakeDummyBuffer;
private HandshakeState handshakeState;
- private AuthorizationResult authorizationResult;
+ private ConnectionAuthContext authContext;
public TlsCryptoSocket(SocketChannel channel, SSLEngine sslEngine) {
this.channel = channel;
@@ -102,13 +97,13 @@ public class TlsCryptoSocket implements CryptoSocket {
channelRead();
break;
case NEED_WORK:
- if (authorizationResult == null) {
- PeerAuthorizerTrustManager.getAuthorizationResult(sslEngine) // only available during handshake
- .ifPresent(result -> {
- if (!result.succeeded()) {
+ if (authContext == null) {
+ PeerAuthorizerTrustManager.getConnectionAuthContext(sslEngine) // only available during handshake
+ .ifPresent(ctx -> {
+ if (!ctx.authorized()) {
metrics.incrementPeerAuthorizationFailures();
}
- authorizationResult = result;
+ authContext = ctx;
});
}
break;
@@ -149,7 +144,7 @@ public class TlsCryptoSocket implements CryptoSocket {
}
} catch (SSLHandshakeException e) {
// sslEngine.getDelegatedTask().run() and handshakeWrap() may throw SSLHandshakeException, potentially handshakeUnwrap() and sslEngine.beginHandshake() as well.
- if (authorizationResult == null || authorizationResult.succeeded()) { // don't include handshake failures due from PeerAuthorizerTrustManager
+ if (authContext == null || authContext.authorized()) { // don't include handshake failures due from PeerAuthorizerTrustManager
metrics.incrementTlsCertificateVerificationFailures();
}
throw e;
@@ -224,19 +219,9 @@ public class TlsCryptoSocket implements CryptoSocket {
}
@Override
- public Optional<SecurityContext> getSecurityContext() {
- try {
- if (handshakeState != HandshakeState.COMPLETED) {
- return Optional.empty();
- }
- List<X509Certificate> peerCertificateChain =
- Arrays.stream(sslEngine.getSession().getPeerCertificates())
- .map(X509Certificate.class::cast)
- .collect(toList());
- return Optional.of(new SecurityContext(peerCertificateChain));
- } catch (SSLPeerUnverifiedException e) { // unverified peer: non-certificate based ciphers or peer did not provide a certificate
- return Optional.of(new SecurityContext(List.of())); // secure connection, but peer does not have a certificate chain.
- }
+ public Optional<ConnectionAuthContext> getConnectionAuthContext() {
+ if (handshakeState != HandshakeState.COMPLETED) return Optional.empty();
+ return Optional.ofNullable(authContext);
}
private boolean handshakeWrap() throws IOException {
diff --git a/jrt/tests/com/yahoo/jrt/EchoTest.java b/jrt/tests/com/yahoo/jrt/EchoTest.java
index 26d4315fad6..e6243eaf4da 100644
--- a/jrt/tests/com/yahoo/jrt/EchoTest.java
+++ b/jrt/tests/com/yahoo/jrt/EchoTest.java
@@ -2,6 +2,7 @@
package com.yahoo.jrt;
+import com.yahoo.security.tls.authz.ConnectionAuthContext;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
@@ -28,19 +29,19 @@ public class EchoTest {
Supervisor client;
Target target;
Values refValues;
- SecurityContext securityContext;
+ ConnectionAuthContext connAuthCtx;
private interface MetricsAssertions {
void assertMetrics(TransportMetrics.Snapshot snapshot) throws AssertionError;
}
- private interface SecurityContextAssertion {
- void assertSecurityContext(SecurityContext securityContext) throws AssertionError;
+ private interface ConnectionAuthContextAssertion {
+ void assertConnectionAuthContext(ConnectionAuthContext authContext) throws AssertionError;
}
@Parameter(value = 0) public CryptoEngine crypto;
@Parameter(value = 1) public MetricsAssertions metricsAssertions;
- @Parameter(value = 2) public SecurityContextAssertion securityContextAssertion;
+ @Parameter(value = 2) public ConnectionAuthContextAssertion connAuthCtxAssertion;
@Parameters(name = "{0}") public static Object[] engines() {
@@ -62,7 +63,7 @@ public class EchoTest {
assertEquals(1, metrics.serverTlsConnectionsEstablished());
assertEquals(1, metrics.clientTlsConnectionsEstablished());
},
- (SecurityContextAssertion) context -> {
+ (ConnectionAuthContextAssertion) context -> {
List<X509Certificate> chain = context.peerCertificateChain();
assertEquals(1, chain.size());
assertEquals(CryptoUtils.certificate, chain.get(0));
@@ -80,7 +81,7 @@ public class EchoTest {
assertEquals(1, metrics.serverTlsConnectionsEstablished());
assertEquals(1, metrics.clientTlsConnectionsEstablished());
},
- (SecurityContextAssertion) context -> {
+ (ConnectionAuthContextAssertion) context -> {
List<X509Certificate> chain = context.peerCertificateChain();
assertEquals(1, chain.size());
assertEquals(CryptoUtils.certificate, chain.get(0));
@@ -146,7 +147,7 @@ public class EchoTest {
for (int i = 0; i < p.size(); i++) {
r.add(p.get(i));
}
- securityContext = req.target().getSecurityContext().orElse(null);
+ connAuthCtx = req.target().getConnectionAuthContext().orElse(null);
}
@org.junit.Test
@@ -164,11 +165,11 @@ public class EchoTest {
if (metricsAssertions != null) {
metricsAssertions.assertMetrics(metrics.snapshot().changesSince(startSnapshot));
}
- if (securityContextAssertion != null) {
- assertNotNull(securityContext);
- securityContextAssertion.assertSecurityContext(securityContext);
+ if (connAuthCtxAssertion != null) {
+ assertNotNull(connAuthCtx);
+ connAuthCtxAssertion.assertConnectionAuthContext(connAuthCtx);
} else {
- assertNull(securityContext);
+ assertNull(connAuthCtx);
}
}
}
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/ConfigFileBasedTlsContext.java b/security-utils/src/main/java/com/yahoo/security/tls/ConfigFileBasedTlsContext.java
index 6d4684666ea..cc664786734 100644
--- a/security-utils/src/main/java/com/yahoo/security/tls/ConfigFileBasedTlsContext.java
+++ b/security-utils/src/main/java/com/yahoo/security/tls/ConfigFileBasedTlsContext.java
@@ -7,7 +7,6 @@ import com.yahoo.security.KeyUtils;
import com.yahoo.security.SslContextBuilder;
import com.yahoo.security.X509CertificateUtils;
import com.yahoo.security.tls.authz.PeerAuthorizerTrustManager;
-import com.yahoo.security.tls.policy.AuthorizedPeers;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
@@ -19,7 +18,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.time.Duration;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -112,9 +110,8 @@ public class ConfigFileBasedTlsContext implements TlsContext {
PeerAuthentication peerAuthentication) {
HostnameVerification hostnameVerification = options.isHostnameValidationDisabled() ? HostnameVerification.DISABLED : HostnameVerification.ENABLED;
- PeerAuthorizerTrustManager authorizerTrustManager = options.getAuthorizedPeers()
- .map(authorizedPeers -> new PeerAuthorizerTrustManager(authorizedPeers, mode, hostnameVerification, mutableTrustManager))
- .orElseGet(() -> new PeerAuthorizerTrustManager(new AuthorizedPeers(Collections.emptySet()), AuthorizationMode.DISABLE, hostnameVerification, mutableTrustManager));
+ PeerAuthorizerTrustManager authorizerTrustManager =
+ new PeerAuthorizerTrustManager(options.getAuthorizedPeers(), mode, hostnameVerification, mutableTrustManager);
SSLContext sslContext = new SslContextBuilder()
.withKeyManager(mutableKeyManager)
.withTrustManager(authorizerTrustManager)
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java b/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java
index a01283318b6..c2ee573dfc6 100644
--- a/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java
+++ b/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java
@@ -11,7 +11,6 @@ import javax.net.ssl.SSLParameters;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
@@ -136,13 +135,9 @@ public class DefaultTlsContext implements TlsContext {
if (!caCertificates.isEmpty()) {
builder.withTrustStore(caCertificates);
}
- if (authorizedPeers != null) {
- builder.withTrustManagerFactory(truststore -> new PeerAuthorizerTrustManager(authorizedPeers, mode, hostnameVerification, truststore));
- } else {
- builder.withTrustManagerFactory(truststore -> new PeerAuthorizerTrustManager(
- new AuthorizedPeers(Collections.emptySet()), AuthorizationMode.DISABLE, hostnameVerification, truststore));
- }
- return builder.build();
+ return builder.withTrustManagerFactory(truststore ->
+ new PeerAuthorizerTrustManager(authorizedPeers, mode, hostnameVerification, truststore))
+ .build();
}
}
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityOptions.java b/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityOptions.java
index 65c87bee0b9..a8802b7f0d3 100644
--- a/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityOptions.java
+++ b/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityOptions.java
@@ -55,9 +55,7 @@ public class TransportSecurityOptions {
return Optional.ofNullable(caCertificatesFile);
}
- public Optional<AuthorizedPeers> getAuthorizedPeers() {
- return Optional.ofNullable(authorizedPeers);
- }
+ public AuthorizedPeers getAuthorizedPeers() { return authorizedPeers; }
public List<String> getAcceptedCiphers() { return acceptedCiphers; }
@@ -96,7 +94,7 @@ public class TransportSecurityOptions {
private Path privateKeyFile;
private Path certificatesFile;
private Path caCertificatesFile;
- private AuthorizedPeers authorizedPeers;
+ private AuthorizedPeers authorizedPeers = AuthorizedPeers.empty();
private List<String> acceptedCiphers = new ArrayList<>();
private boolean isHostnameValidationDisabled;
private List<String> acceptedProtocols = new ArrayList<>();
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/authz/AuthorizationResult.java b/security-utils/src/main/java/com/yahoo/security/tls/authz/AuthorizationResult.java
deleted file mode 100644
index 6fa97e30d63..00000000000
--- a/security-utils/src/main/java/com/yahoo/security/tls/authz/AuthorizationResult.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.security.tls.authz;
-
-import java.util.Collections;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * @author bjorncs
- */
-public class AuthorizationResult {
- private final Set<String> matchedPolicies;
-
- public AuthorizationResult(Set<String> matchedPolicies) {
- this.matchedPolicies = Collections.unmodifiableSet(matchedPolicies);
- }
-
- public Set<String> matchedPolicies() {
- return matchedPolicies;
- }
-
- public boolean succeeded() {
- return matchedPolicies.size() > 0;
- }
-
- @Override
- public String toString() {
- return "AuthorizationResult{" +
- ", matchedPolicies=" + matchedPolicies +
- '}';
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- AuthorizationResult that = (AuthorizationResult) o;
- return Objects.equals(matchedPolicies, that.matchedPolicies);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(matchedPolicies);
- }
-}
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/authz/ConnectionAuthContext.java b/security-utils/src/main/java/com/yahoo/security/tls/authz/ConnectionAuthContext.java
new file mode 100644
index 00000000000..877ba4e74bd
--- /dev/null
+++ b/security-utils/src/main/java/com/yahoo/security/tls/authz/ConnectionAuthContext.java
@@ -0,0 +1,26 @@
+package com.yahoo.security.tls.authz;
+
+import com.yahoo.security.tls.policy.CapabilitySet;
+
+import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author bjorncs
+ */
+public record ConnectionAuthContext(List<X509Certificate> peerCertificateChain,
+ CapabilitySet capabilities,
+ Set<String> matchedPolicies) {
+
+ public ConnectionAuthContext {
+ if (peerCertificateChain.isEmpty()) throw new IllegalArgumentException("Peer certificate chain is empty");
+ peerCertificateChain = List.copyOf(peerCertificateChain);
+ matchedPolicies = Set.copyOf(matchedPolicies);
+ }
+
+ public boolean authorized() { return !capabilities.hasNone(); }
+
+ public X509Certificate peerCertificate() { return peerCertificateChain.get(0); }
+
+}
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizer.java b/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizer.java
index 8c4e87c1de2..a87c578f8c6 100644
--- a/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizer.java
+++ b/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizer.java
@@ -4,6 +4,7 @@ package com.yahoo.security.tls.authz;
import com.yahoo.security.SubjectAlternativeName;
import com.yahoo.security.X509CertificateUtils;
import com.yahoo.security.tls.policy.AuthorizedPeers;
+import com.yahoo.security.tls.policy.CapabilitySet;
import com.yahoo.security.tls.policy.PeerPolicy;
import com.yahoo.security.tls.policy.RequiredPeerCredential;
@@ -34,17 +35,26 @@ public class PeerAuthorizer {
this.authorizedPeers = authorizedPeers;
}
- public AuthorizationResult authorizePeer(X509Certificate peerCertificate) {
+
+ public ConnectionAuthContext authorizePeer(X509Certificate cert) { return authorizePeer(List.of(cert)); }
+
+ public ConnectionAuthContext authorizePeer(List<X509Certificate> certChain) {
+ if (authorizedPeers.isEmpty()) {
+ return new ConnectionAuthContext(certChain, CapabilitySet.all(), Set.of());
+ }
+ X509Certificate cert = certChain.get(0);
Set<String> matchedPolicies = new HashSet<>();
- String cn = getCommonName(peerCertificate).orElse(null);
- List<String> sans = getSubjectAlternativeNames(peerCertificate);
+ Set<CapabilitySet> grantedCapabilities = new HashSet<>();
+ String cn = getCommonName(cert).orElse(null);
+ List<String> sans = getSubjectAlternativeNames(cert);
log.fine(() -> String.format("Subject info from x509 certificate: CN=[%s], 'SAN=%s", cn, sans));
for (PeerPolicy peerPolicy : authorizedPeers.peerPolicies()) {
if (matchesPolicy(peerPolicy, cn, sans)) {
matchedPolicies.add(peerPolicy.policyName());
+ grantedCapabilities.add(peerPolicy.capabilities());
}
}
- return new AuthorizationResult(matchedPolicies);
+ return new ConnectionAuthContext(certChain, CapabilitySet.unionOf(grantedCapabilities), matchedPolicies);
}
private static boolean matchesPolicy(PeerPolicy peerPolicy, String cn, List<String> sans) {
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManager.java b/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManager.java
index e8d558205c4..334216a2c19 100644
--- a/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManager.java
+++ b/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManager.java
@@ -6,6 +6,7 @@ import com.yahoo.security.tls.AuthorizationMode;
import com.yahoo.security.tls.HostnameVerification;
import com.yahoo.security.tls.TrustManagerUtils;
import com.yahoo.security.tls.policy.AuthorizedPeers;
+import com.yahoo.security.tls.policy.CapabilitySet;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
@@ -15,7 +16,9 @@ import java.net.Socket;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.logging.Logger;
/**
@@ -26,7 +29,7 @@ import java.util.logging.Logger;
// Note: Implementation assumes that provided X509ExtendedTrustManager will throw IllegalArgumentException when chain is empty or null
public class PeerAuthorizerTrustManager extends X509ExtendedTrustManager {
- public static final String HANDSHAKE_SESSION_AUTHZ_RESULT_PROPERTY = "vespa.tls.authorization.result";
+ public static final String HANDSHAKE_SESSION_AUTH_CONTEXT_PROPERTY = "vespa.tls.auth.ctx";
private static final Logger log = Logger.getLogger(PeerAuthorizerTrustManager.class.getName());
@@ -55,39 +58,39 @@ public class PeerAuthorizerTrustManager extends X509ExtendedTrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
defaultTrustManager.checkClientTrusted(chain, authType);
- authorizePeer(chain[0], authType, true, null);
+ authorizePeer(chain, authType, true, null);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
defaultTrustManager.checkServerTrusted(chain, authType);
- authorizePeer(chain[0], authType, false, null);
+ authorizePeer(chain, authType, false, null);
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
defaultTrustManager.checkClientTrusted(chain, authType, socket);
- authorizePeer(chain[0], authType, true, null);
+ authorizePeer(chain, authType, true, null);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
overrideHostnameVerificationForClient(socket);
defaultTrustManager.checkServerTrusted(chain, authType, socket);
- authorizePeer(chain[0], authType, false, null);
+ authorizePeer(chain, authType, false, null);
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine sslEngine) throws CertificateException {
defaultTrustManager.checkClientTrusted(chain, authType, sslEngine);
- authorizePeer(chain[0], authType, true, sslEngine);
+ authorizePeer(chain, authType, true, sslEngine);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine sslEngine) throws CertificateException {
overrideHostnameVerificationForClient(sslEngine);
defaultTrustManager.checkServerTrusted(chain, authType, sslEngine);
- authorizePeer(chain[0], authType, false, sslEngine);
+ authorizePeer(chain, authType, false, sslEngine);
}
@Override
@@ -98,23 +101,23 @@ public class PeerAuthorizerTrustManager extends X509ExtendedTrustManager {
/**
* Note: The authorization result is only available during handshake. The underlying handshake session is removed once handshake is complete.
*/
- public static Optional<AuthorizationResult> getAuthorizationResult(SSLEngine sslEngine) {
+ public static Optional<ConnectionAuthContext> getConnectionAuthContext(SSLEngine sslEngine) {
return Optional.ofNullable(sslEngine.getHandshakeSession())
- .flatMap(session -> Optional.ofNullable((AuthorizationResult) session.getValue(HANDSHAKE_SESSION_AUTHZ_RESULT_PROPERTY)));
+ .flatMap(session -> Optional.ofNullable((ConnectionAuthContext) session.getValue(HANDSHAKE_SESSION_AUTH_CONTEXT_PROPERTY)));
}
- private void authorizePeer(X509Certificate certificate, String authType, boolean isVerifyingClient, SSLEngine sslEngine) throws CertificateException {
- if (mode == AuthorizationMode.DISABLE) return;
-
- log.fine(() -> "Verifying certificate: " + createInfoString(certificate, authType, isVerifyingClient));
- AuthorizationResult result = authorizer.authorizePeer(certificate);
+ private void authorizePeer(X509Certificate[] certChain, String authType, boolean isVerifyingClient, SSLEngine sslEngine) throws CertificateException {
+ log.fine(() -> "Verifying certificate: " + createInfoString(certChain[0], authType, isVerifyingClient));
+ ConnectionAuthContext result = mode != AuthorizationMode.DISABLE
+ ? authorizer.authorizePeer(List.of(certChain))
+ : new ConnectionAuthContext(List.of(certChain), CapabilitySet.all(), Set.of());
if (sslEngine != null) { // getHandshakeSession() will never return null in this context
- sslEngine.getHandshakeSession().putValue(HANDSHAKE_SESSION_AUTHZ_RESULT_PROPERTY, result);
+ sslEngine.getHandshakeSession().putValue(HANDSHAKE_SESSION_AUTH_CONTEXT_PROPERTY, result);
}
- if (result.succeeded()) {
+ if (result.authorized()) {
log.fine(() -> String.format("Verification result: %s", result));
} else {
- String errorMessage = "Authorization failed: " + createInfoString(certificate, authType, isVerifyingClient);
+ String errorMessage = "Authorization failed: " + createInfoString(certChain[0], authType, isVerifyingClient);
log.warning(errorMessage);
if (mode == AuthorizationMode.ENFORCE) {
throw new CertificateException(errorMessage);
@@ -122,9 +125,10 @@ public class PeerAuthorizerTrustManager extends X509ExtendedTrustManager {
}
}
- private static String createInfoString(X509Certificate certificate, String authType, boolean isVerifyingClient) {
- return String.format("DN='%s', SANs=%s, authType='%s', isVerifyingClient='%b'",
- certificate.getSubjectX500Principal(), X509CertificateUtils.getSubjectAlternativeNames(certificate), authType, isVerifyingClient);
+ private String createInfoString(X509Certificate certificate, String authType, boolean isVerifyingClient) {
+ return String.format("DN='%s', SANs=%s, authType='%s', isVerifyingClient='%b', mode=%s",
+ certificate.getSubjectX500Principal(), X509CertificateUtils.getSubjectAlternativeNames(certificate),
+ authType, isVerifyingClient, mode);
}
private void overrideHostnameVerificationForClient(SSLEngine engine) {
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/json/TransportSecurityOptionsJsonSerializer.java b/security-utils/src/main/java/com/yahoo/security/tls/json/TransportSecurityOptionsJsonSerializer.java
index 195e200febb..fcd84056212 100644
--- a/security-utils/src/main/java/com/yahoo/security/tls/json/TransportSecurityOptionsJsonSerializer.java
+++ b/security-utils/src/main/java/com/yahoo/security/tls/json/TransportSecurityOptionsJsonSerializer.java
@@ -144,28 +144,27 @@ public class TransportSecurityOptionsJsonSerializer {
options.getCaCertificatesFile().ifPresent(value -> entity.files.caCertificatesFile = value.toString());
options.getCertificatesFile().ifPresent(value -> entity.files.certificatesFile = value.toString());
options.getPrivateKeyFile().ifPresent(value -> entity.files.privateKeyFile = value.toString());
- options.getAuthorizedPeers().ifPresent( authorizedPeers -> entity.authorizedPeers =
- authorizedPeers.peerPolicies().stream()
- // Makes tests stable
- .sorted(Comparator.comparing(PeerPolicy::policyName))
- .map(peerPolicy -> {
- AuthorizedPeer authorizedPeer = new AuthorizedPeer();
- authorizedPeer.name = peerPolicy.policyName();
- authorizedPeer.requiredCredentials = new ArrayList<>();
- authorizedPeer.description = peerPolicy.description().orElse(null);
- CapabilitySet caps = peerPolicy.capabilities();
- if (!caps.hasAllCapabilities()) {
- authorizedPeer.capabilities = List.copyOf(caps.toCapabilityNames());
- }
- for (RequiredPeerCredential requiredPeerCredential : peerPolicy.requiredCredentials()) {
- RequiredCredential requiredCredential = new RequiredCredential();
- requiredCredential.field = toField(requiredPeerCredential.field());
- requiredCredential.matchExpression = requiredPeerCredential.pattern().asString();
- authorizedPeer.requiredCredentials.add(requiredCredential);
- }
- return authorizedPeer;
- })
- .collect(toList()));
+ entity.authorizedPeers = options.getAuthorizedPeers().peerPolicies().stream()
+ // Makes tests stable
+ .sorted(Comparator.comparing(PeerPolicy::policyName))
+ .map(peerPolicy -> {
+ AuthorizedPeer authorizedPeer = new AuthorizedPeer();
+ authorizedPeer.name = peerPolicy.policyName();
+ authorizedPeer.requiredCredentials = new ArrayList<>();
+ authorizedPeer.description = peerPolicy.description().orElse(null);
+ CapabilitySet caps = peerPolicy.capabilities();
+ if (!caps.hasAll()) {
+ authorizedPeer.capabilities = List.copyOf(caps.toNames());
+ }
+ for (RequiredPeerCredential requiredPeerCredential : peerPolicy.requiredCredentials()) {
+ RequiredCredential requiredCredential = new RequiredCredential();
+ requiredCredential.field = toField(requiredPeerCredential.field());
+ requiredCredential.matchExpression = requiredPeerCredential.pattern().asString();
+ authorizedPeer.requiredCredentials.add(requiredCredential);
+ }
+ return authorizedPeer;
+ })
+ .toList();
if (!options.getAcceptedCiphers().isEmpty()) {
entity.acceptedCiphers = options.getAcceptedCiphers();
}
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/policy/AuthorizedPeers.java b/security-utils/src/main/java/com/yahoo/security/tls/policy/AuthorizedPeers.java
index 136022e2ed9..5e49a5b341c 100644
--- a/security-utils/src/main/java/com/yahoo/security/tls/policy/AuthorizedPeers.java
+++ b/security-utils/src/main/java/com/yahoo/security/tls/policy/AuthorizedPeers.java
@@ -8,10 +8,14 @@ import java.util.Set;
*/
public record AuthorizedPeers(Set<PeerPolicy> peerPolicies) {
+ private static final AuthorizedPeers EMPTY = new AuthorizedPeers(Set.of());
+
public AuthorizedPeers {
peerPolicies = verifyPeerPolicies(peerPolicies);
}
+ public static AuthorizedPeers empty() { return EMPTY; }
+
private static Set<PeerPolicy> verifyPeerPolicies(Set<PeerPolicy> peerPolicies) {
long distinctNames = peerPolicies.stream()
.map(PeerPolicy::policyName)
@@ -23,4 +27,6 @@ public record AuthorizedPeers(Set<PeerPolicy> peerPolicies) {
return Set.copyOf(peerPolicies);
}
+ public boolean isEmpty() { return peerPolicies.isEmpty(); }
+
}
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/policy/CapabilitySet.java b/security-utils/src/main/java/com/yahoo/security/tls/policy/CapabilitySet.java
index 44ff1eedfb0..50de98c621c 100644
--- a/security-utils/src/main/java/com/yahoo/security/tls/policy/CapabilitySet.java
+++ b/security-utils/src/main/java/com/yahoo/security/tls/policy/CapabilitySet.java
@@ -3,10 +3,12 @@ package com.yahoo.security.tls.policy;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;
@@ -56,18 +58,27 @@ public class CapabilitySet {
return new CapabilitySet(caps);
}
+ public static CapabilitySet unionOf(Collection<CapabilitySet> capSets) {
+ EnumSet<Capability> union = EnumSet.noneOf(Capability.class);
+ capSets.forEach(cs -> union.addAll(cs.caps));
+ return new CapabilitySet(union);
+ }
+
public static CapabilitySet from(EnumSet<Capability> caps) { return new CapabilitySet(EnumSet.copyOf(caps)); }
public static CapabilitySet from(Collection<Capability> caps) { return new CapabilitySet(EnumSet.copyOf(caps)); }
public static CapabilitySet from(Capability... caps) { return new CapabilitySet(EnumSet.copyOf(List.of(caps))); }
public static CapabilitySet all() { return ALL_CAPABILITIES; }
public static CapabilitySet none() { return NO_CAPABILITIES; }
- public boolean hasAllCapabilities() { return this.caps.equals(ALL_CAPABILITIES.caps); }
+ public boolean hasAll() { return this.caps.equals(ALL_CAPABILITIES.caps); }
+ public boolean hasNone() { return this.caps.equals(NO_CAPABILITIES.caps); }
- public SortedSet<String> toCapabilityNames() {
+ public SortedSet<String> toNames() {
return caps.stream().map(Capability::asString).collect(Collectors.toCollection(TreeSet::new));
}
+ public Set<Capability> asSet() { return Collections.unmodifiableSet(caps); }
+
@Override
public String toString() {
return "CapabilitySet{" +
diff --git a/security-utils/src/test/java/com/yahoo/security/tls/authz/PeerAuthorizerTest.java b/security-utils/src/test/java/com/yahoo/security/tls/authz/PeerAuthorizerTest.java
index 5a4ae1f4ff6..3791aed4155 100644
--- a/security-utils/src/test/java/com/yahoo/security/tls/authz/PeerAuthorizerTest.java
+++ b/security-utils/src/test/java/com/yahoo/security/tls/authz/PeerAuthorizerTest.java
@@ -6,6 +6,8 @@ import com.yahoo.security.KeyUtils;
import com.yahoo.security.SubjectAlternativeName.Type;
import com.yahoo.security.X509CertificateBuilder;
import com.yahoo.security.tls.policy.AuthorizedPeers;
+import com.yahoo.security.tls.policy.Capability;
+import com.yahoo.security.tls.policy.CapabilitySet;
import com.yahoo.security.tls.policy.PeerPolicy;
import com.yahoo.security.tls.policy.RequiredPeerCredential;
import com.yahoo.security.tls.policy.RequiredPeerCredential.Field;
@@ -19,6 +21,8 @@ import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
+import java.util.Set;
import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_ECDSA;
import static com.yahoo.security.tls.policy.RequiredPeerCredential.Field.CN;
@@ -46,7 +50,7 @@ public class PeerAuthorizerTest {
RequiredPeerCredential sanRequirement = createRequiredCredential(SAN_DNS, "*.matching.san");
PeerAuthorizer authorizer = createPeerAuthorizer(createPolicy(POLICY_1, cnRequirement, sanRequirement));
- AuthorizationResult result = authorizer.authorizePeer(createCertificate("foo.matching.cn", asList("foo.matching.san", "foo.invalid.san"), emptyList()));
+ ConnectionAuthContext result = authorizer.authorizePeer(createCertificate("foo.matching.cn", asList("foo.matching.san", "foo.invalid.san"), emptyList()));
assertAuthorized(result);
assertThat(result.matchedPolicies()).containsOnly(POLICY_1);
@@ -64,7 +68,7 @@ public class PeerAuthorizerTest {
createPolicy(POLICY_1, cnRequirement, sanRequirement),
createPolicy(POLICY_2, cnRequirement, sanRequirement));
- AuthorizationResult result = peerAuthorizer
+ ConnectionAuthContext result = peerAuthorizer
.authorizePeer(createCertificate("foo.matching.cn", singletonList("foo.matching.san"), emptyList()));
assertAuthorized(result);
assertThat(result.matchedPolicies()).containsOnly(POLICY_1, POLICY_2);
@@ -76,7 +80,7 @@ public class PeerAuthorizerTest {
createPolicy(POLICY_1, createRequiredCredential(CN, "*.matching.cn")),
createPolicy(POLICY_2, createRequiredCredential(SAN_DNS, "*.matching.san")));
- AuthorizationResult result = peerAuthorizer.authorizePeer(createCertificate("foo.invalid.cn", singletonList("foo.matching.san"), emptyList()));
+ ConnectionAuthContext result = peerAuthorizer.authorizePeer(createCertificate("foo.invalid.cn", singletonList("foo.matching.san"), emptyList()));
assertAuthorized(result);
assertThat(result.matchedPolicies()).containsOnly(POLICY_2);
}
@@ -101,13 +105,28 @@ public class PeerAuthorizerTest {
RequiredPeerCredential sanUriRequirement = createRequiredCredential(SAN_URI, "myscheme://my/*/uri");
PeerAuthorizer authorizer = createPeerAuthorizer(createPolicy(POLICY_1, cnRequirement, sanUriRequirement));
- AuthorizationResult result = authorizer.authorizePeer(createCertificate("foo.matching.cn", singletonList("foo.irrelevant.san"), singletonList("myscheme://my/matching/uri")));
+ ConnectionAuthContext result = authorizer.authorizePeer(createCertificate("foo.matching.cn", singletonList("foo.irrelevant.san"), singletonList("myscheme://my/matching/uri")));
assertAuthorized(result);
assertThat(result.matchedPolicies()).containsOnly(POLICY_1);
assertUnauthorized(authorizer.authorizePeer(createCertificate("foo.matching.cn", emptyList(), singletonList("myscheme://my/nonmatching/url"))));
}
+ @Test
+ public void auth_context_contains_union_of_granted_capabilities_from_policies() {
+ RequiredPeerCredential cnRequirement = createRequiredCredential(CN, "*.matching.cn");
+ RequiredPeerCredential sanRequirement = createRequiredCredential(SAN_DNS, "*.matching.san");
+
+ PeerAuthorizer peerAuthorizer = createPeerAuthorizer(
+ createPolicy(POLICY_1, List.of(Capability.SLOBROK__API, Capability.CONTENT__DOCUMENT_API), List.of(cnRequirement)),
+ createPolicy(POLICY_2, List.of(Capability.SLOBROK__API, Capability.CONTENT__SEARCH_API), List.of(sanRequirement)));
+
+ var result = peerAuthorizer
+ .authorizePeer(createCertificate("foo.matching.cn", List.of("foo.matching.san"), List.of()));
+ assertAuthorized(result);
+ assertCapabiltiesGranted(result, Set.of(Capability.SLOBROK__API, Capability.CONTENT__DOCUMENT_API, Capability.CONTENT__SEARCH_API));
+ }
+
private static X509Certificate createCertificate(String subjectCn, List<String> sanDns, List<String> sanUri) {
X509CertificateBuilder builder =
X509CertificateBuilder.fromKeypair(
@@ -134,12 +153,20 @@ public class PeerAuthorizerTest {
return new PeerPolicy(name, asList(requiredCredentials));
}
- private static void assertAuthorized(AuthorizationResult result) {
- assertTrue(result.succeeded());
+ private static PeerPolicy createPolicy(String name, List<Capability> caps, List<RequiredPeerCredential> creds) {
+ return new PeerPolicy(name, Optional.empty(), CapabilitySet.from(caps), creds);
+ }
+
+ private static void assertAuthorized(ConnectionAuthContext result) {
+ assertTrue(result.authorized());
+ }
+
+ private static void assertUnauthorized(ConnectionAuthContext result) {
+ assertFalse(result.authorized());
}
- private static void assertUnauthorized(AuthorizationResult result) {
- assertFalse(result.succeeded());
+ private static void assertCapabiltiesGranted(ConnectionAuthContext ctx, Set<Capability> expected) {
+ assertThat(ctx.capabilities().asSet()).containsOnly(expected.toArray(new Capability[0]));
}
}
diff --git a/security-utils/src/test/java/com/yahoo/security/tls/policy/CapabilitySetTest.java b/security-utils/src/test/java/com/yahoo/security/tls/policy/CapabilitySetTest.java
index 3379c37e918..429e5b24513 100644
--- a/security-utils/src/test/java/com/yahoo/security/tls/policy/CapabilitySetTest.java
+++ b/security-utils/src/test/java/com/yahoo/security/tls/policy/CapabilitySetTest.java
@@ -20,7 +20,7 @@ class CapabilitySetTest {
SortedSet<String> expectedNames = Arrays.stream(Capability.values())
.map(Capability::asString)
.collect(Collectors.toCollection(TreeSet::new));
- SortedSet<String> actualNames = CapabilitySet.all().toCapabilityNames();
+ SortedSet<String> actualNames = CapabilitySet.all().toNames();
assertEquals(expectedNames, actualNames);
}