Skip to content

Commit 5de8d88

Browse files
committed
feat(xds): Implement request builder for external authorization
This commit introduces the `CheckRequestBuilder` library, which is responsible for constructing the `CheckRequest` message sent to the external authorization service. The `CheckRequestBuilder` gathers information from various sources, including: - `ServerCall` attributes (local and remote addresses, SSL session). - `MethodDescriptor` (full method name). - Request headers. It uses this information to populate the `AttributeContext` of the `CheckRequest` message, which provides the authorization service with the necessary context to make an authorization decision. This commit also introduces the `ExtAuthzCertificateProvider`, a helper class for extracting certificate information, such as the principal and PEM-encoded certificate. Unit tests for the new components are also included.
1 parent 5654c64 commit 5de8d88

File tree

4 files changed

+967
-0
lines changed

4 files changed

+967
-0
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright 2025 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.xds.internal.extauthz;
18+
19+
import com.google.common.io.BaseEncoding;
20+
import java.io.UnsupportedEncodingException;
21+
import java.net.URLEncoder;
22+
import java.nio.charset.StandardCharsets;
23+
import java.security.cert.CertificateEncodingException;
24+
import java.security.cert.X509Certificate;
25+
import java.util.Collection;
26+
import java.util.List;
27+
import java.util.logging.Level;
28+
import java.util.logging.Logger;
29+
30+
/**
31+
* A utility class for certificate-related information.
32+
*/
33+
public final class CertificateUtils {
34+
private static final Logger logger = Logger.getLogger(CertificateUtils.class.getName());
35+
// From RFC 5280, section 4.2.1.6, Subject Alternative Name
36+
// dNSName (2)
37+
// iPAddress (7)
38+
private static final int SAN_TYPE_DNS_NAME = 2;
39+
private static final int SAN_TYPE_IP_ADDRESS = 7;
40+
41+
private CertificateUtils() {}
42+
43+
/**
44+
* Gets the principal from a certificate. It returns the cert's first IP Address SAN if set,
45+
* otherwise the cert's first DNS SAN if set, otherwise the subject field of the certificate in
46+
* RFC 2253 format.
47+
*
48+
* @param cert The certificate.
49+
* @return The principal.
50+
*/
51+
public static String getPrincipal(X509Certificate cert) {
52+
try {
53+
Collection<List<?>> sans = cert.getSubjectAlternativeNames();
54+
if (sans != null) {
55+
// Look for IP Address SAN.
56+
for (List<?> san : sans) {
57+
if (san.size() == 2 && san.get(0) instanceof Integer
58+
&& (Integer) san.get(0) == SAN_TYPE_IP_ADDRESS) {
59+
return (String) san.get(1);
60+
}
61+
}
62+
// If no IP Address SAN, look for DNS SAN.
63+
for (List<?> san : sans) {
64+
if (san.size() == 2 && san.get(0) instanceof Integer
65+
&& (Integer) san.get(0) == SAN_TYPE_DNS_NAME) {
66+
return (String) san.get(1);
67+
}
68+
}
69+
}
70+
} catch (java.security.cert.CertificateParsingException e) {
71+
logger.log(Level.WARNING, "Error parsing certificate SANs. This is not expected, "
72+
+ "falling back to the subject according to the spec.", e);
73+
}
74+
return cert.getSubjectX500Principal().getName();
75+
}
76+
77+
/**
78+
* Gets the URL PEM encoded certificate. It Pem encodes first and then urlencodes.
79+
*
80+
* @param cert The certificate.
81+
* @return The URL PEM encoded certificate.
82+
* @throws CertificateEncodingException If an error occurs while encoding the certificate.
83+
* @throws UnsupportedEncodingException If an error occurs while encoding the URL.
84+
*/
85+
public static String getUrlPemEncodedCertificate(X509Certificate cert)
86+
throws CertificateEncodingException, UnsupportedEncodingException {
87+
String pemCert = CertPemConverter.toPem(cert);
88+
return URLEncoder.encode(pemCert, StandardCharsets.UTF_8.toString());
89+
}
90+
91+
/**
92+
* A utility class for PEM encoding.
93+
*/
94+
private static final class CertPemConverter {
95+
96+
private static final String X509_PEM_HEADER = "-----BEGIN CERTIFICATE-----\n";
97+
private static final String X509_PEM_FOOTER = "\n-----END CERTIFICATE-----\n";
98+
99+
private CertPemConverter() {}
100+
101+
/**
102+
* Converts a certificate to a PEM string.
103+
*
104+
* @param cert The certificate to convert.
105+
* @return The PEM encoded certificate.
106+
* @throws CertificateEncodingException If an error occurs while encoding the certificate.
107+
*/
108+
public static String toPem(X509Certificate cert) throws CertificateEncodingException {
109+
return X509_PEM_HEADER + BaseEncoding.base64().encode(cert.getEncoded()) + X509_PEM_FOOTER;
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)