1616
1717package io .grpc .xds ;
1818
19+ import static com .google .common .base .Preconditions .checkNotNull ;
20+ import static io .grpc .xds .XdsNameResolver .CLUSTER_SELECTION_KEY ;
21+ import static io .grpc .xds .XdsNameResolver .XDS_CONFIG_CALL_OPTION_KEY ;
22+
1923import com .google .auth .oauth2 .ComputeEngineCredentials ;
2024import com .google .auth .oauth2 .IdTokenCredentials ;
25+ import com .google .common .annotations .VisibleForTesting ;
2126import com .google .common .primitives .UnsignedLongs ;
2227import com .google .protobuf .Any ;
2328import com .google .protobuf .InvalidProtocolBufferException ;
3439import io .grpc .Metadata ;
3540import io .grpc .MethodDescriptor ;
3641import io .grpc .Status ;
42+ import io .grpc .StatusOr ;
3743import io .grpc .auth .MoreCallCredentials ;
44+ import io .grpc .xds .GcpAuthenticationFilter .AudienceMetadataParser .AudienceWrapper ;
3845import io .grpc .xds .MetadataRegistry .MetadataValueParser ;
46+ import io .grpc .xds .XdsConfig .XdsClusterConfig ;
3947import io .grpc .xds .client .XdsResourceType .ResourceInvalidException ;
4048import java .util .LinkedHashMap ;
4149import java .util .Map ;
@@ -52,6 +60,13 @@ final class GcpAuthenticationFilter implements Filter {
5260 static final String TYPE_URL =
5361 "type.googleapis.com/envoy.extensions.filters.http.gcp_authn.v3.GcpAuthnFilterConfig" ;
5462
63+ final String filterInstanceName ;
64+
65+ GcpAuthenticationFilter (String name ) {
66+ filterInstanceName = checkNotNull (name , "name" );
67+ }
68+
69+
5570 static final class Provider implements Filter .Provider {
5671 @ Override
5772 public String [] typeUrls () {
@@ -65,7 +80,7 @@ public boolean isClientFilter() {
6580
6681 @ Override
6782 public GcpAuthenticationFilter newInstance (String name ) {
68- return new GcpAuthenticationFilter ();
83+ return new GcpAuthenticationFilter (name );
6984 }
7085
7186 @ Override
@@ -119,34 +134,57 @@ public ClientInterceptor buildClientInterceptor(FilterConfig config,
119134 public <ReqT , RespT > ClientCall <ReqT , RespT > interceptCall (
120135 MethodDescriptor <ReqT , RespT > method , CallOptions callOptions , Channel next ) {
121136
122- /* String clusterName = callOptions.getOption(XdsAttributes.ATTR_CLUSTER_NAME );
137+ String clusterName = callOptions .getOption (CLUSTER_SELECTION_KEY );
123138 if (clusterName == null ) {
139+ return new FailingClientCall <>(
140+ Status .UNAVAILABLE .withDescription (
141+ String .format (
142+ "GCP Authn for %s does not contain cluster resource" , filterInstanceName )));
143+ }
144+
145+ if (!clusterName .startsWith ("cluster:" )) {
124146 return next .newCall (method , callOptions );
125- }*/
126-
127- // TODO: Fetch the CDS resource for the cluster.
128- // If the CDS resource is not available, fail the RPC with Status.UNAVAILABLE.
129-
130- // TODO: Extract the audience from the CDS resource metadata.
131- // If the audience is not found or is in the wrong format, fail the RPC.
132- String audience = "TEST_AUDIENCE" ;
133-
134- try {
135- CallCredentials existingCallCredentials = callOptions .getCredentials ();
136- CallCredentials newCallCredentials =
137- getCallCredentials (callCredentialsCache , audience , credentials );
138- if (existingCallCredentials != null ) {
139- callOptions = callOptions .withCallCredentials (
140- new CompositeCallCredentials (existingCallCredentials , newCallCredentials ));
141- } else {
142- callOptions = callOptions .withCallCredentials (newCallCredentials );
143- }
144147 }
145- catch (Exception e ) {
146- // If we fail to attach CallCredentials due to any reason, return a FailingClientCall
147- return new FailingClientCall <>(Status .UNAUTHENTICATED
148- .withDescription ("Failed to attach CallCredentials." )
149- .withCause (e ));
148+ XdsConfig xdsConfig = callOptions .getOption (XDS_CONFIG_CALL_OPTION_KEY );
149+ if (xdsConfig == null ) {
150+ return new FailingClientCall <>(
151+ Status .UNAVAILABLE .withDescription (
152+ String .format (
153+ "GCP Authn for %s with %s does not contain xds configuration" ,
154+ filterInstanceName , clusterName )));
155+ }
156+ StatusOr <XdsClusterConfig > xdsCluster =
157+ xdsConfig .getClusters ().get (clusterName .substring ("cluster:" .length ()));
158+ if (xdsCluster == null ) {
159+ return new FailingClientCall <>(
160+ Status .UNAVAILABLE .withDescription (
161+ String .format (
162+ "GCP Authn for %s with %s - xds cluster config does not contain xds cluster" ,
163+ filterInstanceName , clusterName )));
164+ }
165+ if (!xdsCluster .hasValue ()) {
166+ return new FailingClientCall <>(xdsCluster .getStatus ());
167+ }
168+ Object audienceObj =
169+ xdsCluster .getValue ().getClusterResource ().parsedMetadata ().get (filterInstanceName );
170+ if (audienceObj == null ) {
171+ return next .newCall (method , callOptions );
172+ }
173+ if (!(audienceObj instanceof AudienceWrapper )) {
174+ return new FailingClientCall <>(
175+ Status .UNAVAILABLE .withDescription (
176+ String .format ("GCP Authn found wrong type in %s metadata: %s=%s" ,
177+ clusterName , filterInstanceName , audienceObj .getClass ())));
178+ }
179+ AudienceWrapper audience = (AudienceWrapper ) audienceObj ;
180+ CallCredentials existingCallCredentials = callOptions .getCredentials ();
181+ CallCredentials newCallCredentials =
182+ getCallCredentials (callCredentialsCache , audience .audience , credentials );
183+ if (existingCallCredentials != null ) {
184+ callOptions = callOptions .withCallCredentials (
185+ new CompositeCallCredentials (existingCallCredentials , newCallCredentials ));
186+ } else {
187+ callOptions = callOptions .withCallCredentials (newCallCredentials );
150188 }
151189 return next .newCall (method , callOptions );
152190 }
@@ -186,9 +224,11 @@ public String typeUrl() {
186224 }
187225
188226 /** An implementation of {@link ClientCall} that fails when started. */
189- private static final class FailingClientCall <ReqT , RespT > extends ClientCall <ReqT , RespT > {
227+ @ VisibleForTesting
228+ static final class FailingClientCall <ReqT , RespT > extends ClientCall <ReqT , RespT > {
190229
191- private final Status error ;
230+ @ VisibleForTesting
231+ final Status error ;
192232
193233 public FailingClientCall (Status error ) {
194234 this .error = error ;
@@ -235,13 +275,21 @@ V getOrInsert(K key, Function<K, V> create) {
235275
236276 static class AudienceMetadataParser implements MetadataValueParser {
237277
278+ static final class AudienceWrapper {
279+ final String audience ;
280+
281+ AudienceWrapper (String audience ) {
282+ this .audience = checkNotNull (audience );
283+ }
284+ }
285+
238286 @ Override
239287 public String getTypeUrl () {
240288 return "type.googleapis.com/envoy.extensions.filters.http.gcp_authn.v3.Audience" ;
241289 }
242290
243291 @ Override
244- public String parse (Any any ) throws ResourceInvalidException {
292+ public AudienceWrapper parse (Any any ) throws ResourceInvalidException {
245293 Audience audience ;
246294 try {
247295 audience = any .unpack (Audience .class );
@@ -253,7 +301,7 @@ public String parse(Any any) throws ResourceInvalidException {
253301 throw new ResourceInvalidException (
254302 "Audience URL is empty. Metadata value must contain a valid URL." );
255303 }
256- return url ;
304+ return new AudienceWrapper ( url ) ;
257305 }
258306 }
259307}
0 commit comments