diff options
Diffstat (limited to 'security-utils/src/main/java/com/yahoo/security/tls/ConnectionAuthContext.java')
-rw-r--r-- | security-utils/src/main/java/com/yahoo/security/tls/ConnectionAuthContext.java | 60 |
1 files changed, 55 insertions, 5 deletions
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 b4e8878fb01..f231e8429ce 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 @@ -7,28 +7,78 @@ import java.security.cert.X509Certificate; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.logging.Logger; import static com.yahoo.security.SubjectAlternativeName.Type.DNS; import static com.yahoo.security.SubjectAlternativeName.Type.URI; +import static com.yahoo.security.tls.CapabilityMode.DISABLE; +import static com.yahoo.security.tls.CapabilityMode.LOG_ONLY; /** * @author bjorncs */ public record ConnectionAuthContext(List<X509Certificate> peerCertificateChain, CapabilitySet capabilities, - Set<String> matchedPolicies) { + Set<String> matchedPolicies, + CapabilityMode capabilityMode) { - private static final ConnectionAuthContext DEFAULT_ALL_CAPABILITIES = new ConnectionAuthContext(List.of()); + private static final Logger log = Logger.getLogger(ConnectionAuthContext.class.getName()); public ConnectionAuthContext { peerCertificateChain = List.copyOf(peerCertificateChain); matchedPolicies = Set.copyOf(matchedPolicies); } - private ConnectionAuthContext(List<X509Certificate> certs) { this(certs, CapabilitySet.all(), Set.of()); } + private ConnectionAuthContext(List<X509Certificate> certs, CapabilityMode capabilityMode) { + this(certs, CapabilitySet.all(), Set.of(), capabilityMode); + } public boolean authorized() { return !capabilities.hasNone(); } + /** Throws checked exception to force caller to handle verification failed. */ + public void verifyCapabilities(CapabilitySet requiredCapabilities) throws MissingCapabilitiesException { + verifyCapabilities(requiredCapabilities, null, null, null); + } + + /** + * Throws checked exception to force caller to handle verification failed. + * Provided strings are used for improved logging only + * */ + public void verifyCapabilities(CapabilitySet requiredCapabilities, String action, String resource, String peer) + throws MissingCapabilitiesException { + if (capabilityMode == DISABLE) return; + boolean hasCapabilities = capabilities.has(requiredCapabilities); + if (!hasCapabilities) { + String msg = createPermissionDeniedErrorMessage(requiredCapabilities, action, resource, peer); + if (capabilityMode == LOG_ONLY) { + log.info(msg); + } else { + // Ideally log as warning, but we have no mechanism for de-duplicating repeated log spamming. + log.fine(msg); + throw new MissingCapabilitiesException(msg); + } + } + } + + String createPermissionDeniedErrorMessage( + CapabilitySet required, String action, String resource, String peer) { + StringBuilder b = new StringBuilder(); + if (capabilityMode == LOG_ONLY) b.append("Dry-run: "); + b.append("Permission denied"); + if (resource != null) { + b.append(" for '"); + if (action != null) { + b.append(action).append("' on '"); + } + b.append(resource).append("'"); + } + b.append(". Peer "); + if (peer != null) b.append("'").append(peer).append("' "); + return b.append("with ").append(peerCertificateString().orElse("<missing-certificate>")).append(". Requires capabilities ") + .append(required.toNames()).append(" but peer has ").append(capabilities.toNames()) + .append(".").toString(); + } + public Optional<X509Certificate> peerCertificate() { return peerCertificateChain.isEmpty() ? Optional.empty() : Optional.of(peerCertificateChain.get(0)); } @@ -62,11 +112,11 @@ public record ConnectionAuthContext(List<X509Certificate> peerCertificateChain, } /** Construct instance with all capabilities */ - public static ConnectionAuthContext defaultAllCapabilities() { return DEFAULT_ALL_CAPABILITIES; } + public static ConnectionAuthContext defaultAllCapabilities() { return new ConnectionAuthContext(List.of(), DISABLE); } /** Construct instance with all capabilities */ public static ConnectionAuthContext defaultAllCapabilities(List<X509Certificate> certs) { - return new ConnectionAuthContext(certs); + return new ConnectionAuthContext(certs, DISABLE); } } |