16
16
17
17
package io .grpc .xds ;
18
18
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
+
19
23
import com .google .auth .oauth2 .ComputeEngineCredentials ;
20
24
import com .google .auth .oauth2 .IdTokenCredentials ;
25
+ import com .google .common .annotations .VisibleForTesting ;
21
26
import com .google .common .primitives .UnsignedLongs ;
22
27
import com .google .protobuf .Any ;
23
28
import com .google .protobuf .InvalidProtocolBufferException ;
34
39
import io .grpc .Metadata ;
35
40
import io .grpc .MethodDescriptor ;
36
41
import io .grpc .Status ;
42
+ import io .grpc .StatusOr ;
37
43
import io .grpc .auth .MoreCallCredentials ;
44
+ import io .grpc .xds .GcpAuthenticationFilter .AudienceMetadataParser .AudienceWrapper ;
38
45
import io .grpc .xds .MetadataRegistry .MetadataValueParser ;
46
+ import io .grpc .xds .XdsConfig .XdsClusterConfig ;
39
47
import io .grpc .xds .client .XdsResourceType .ResourceInvalidException ;
40
48
import java .util .LinkedHashMap ;
41
49
import java .util .Map ;
@@ -52,6 +60,13 @@ final class GcpAuthenticationFilter implements Filter {
52
60
static final String TYPE_URL =
53
61
"type.googleapis.com/envoy.extensions.filters.http.gcp_authn.v3.GcpAuthnFilterConfig" ;
54
62
63
+ final String filterInstanceName ;
64
+
65
+ GcpAuthenticationFilter (String name ) {
66
+ filterInstanceName = checkNotNull (name , "name" );
67
+ }
68
+
69
+
55
70
static final class Provider implements Filter .Provider {
56
71
@ Override
57
72
public String [] typeUrls () {
@@ -65,7 +80,7 @@ public boolean isClientFilter() {
65
80
66
81
@ Override
67
82
public GcpAuthenticationFilter newInstance (String name ) {
68
- return new GcpAuthenticationFilter ();
83
+ return new GcpAuthenticationFilter (name );
69
84
}
70
85
71
86
@ Override
@@ -119,34 +134,57 @@ public ClientInterceptor buildClientInterceptor(FilterConfig config,
119
134
public <ReqT , RespT > ClientCall <ReqT , RespT > interceptCall (
120
135
MethodDescriptor <ReqT , RespT > method , CallOptions callOptions , Channel next ) {
121
136
122
- /* String clusterName = callOptions.getOption(XdsAttributes.ATTR_CLUSTER_NAME );
137
+ String clusterName = callOptions .getOption (CLUSTER_SELECTION_KEY );
123
138
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:" )) {
124
146
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
- }
144
147
}
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 );
150
188
}
151
189
return next .newCall (method , callOptions );
152
190
}
@@ -186,9 +224,11 @@ public String typeUrl() {
186
224
}
187
225
188
226
/** 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 > {
190
229
191
- private final Status error ;
230
+ @ VisibleForTesting
231
+ final Status error ;
192
232
193
233
public FailingClientCall (Status error ) {
194
234
this .error = error ;
@@ -235,13 +275,21 @@ V getOrInsert(K key, Function<K, V> create) {
235
275
236
276
static class AudienceMetadataParser implements MetadataValueParser {
237
277
278
+ static final class AudienceWrapper {
279
+ final String audience ;
280
+
281
+ AudienceWrapper (String audience ) {
282
+ this .audience = checkNotNull (audience );
283
+ }
284
+ }
285
+
238
286
@ Override
239
287
public String getTypeUrl () {
240
288
return "type.googleapis.com/envoy.extensions.filters.http.gcp_authn.v3.Audience" ;
241
289
}
242
290
243
291
@ Override
244
- public String parse (Any any ) throws ResourceInvalidException {
292
+ public AudienceWrapper parse (Any any ) throws ResourceInvalidException {
245
293
Audience audience ;
246
294
try {
247
295
audience = any .unpack (Audience .class );
@@ -253,7 +301,7 @@ public String parse(Any any) throws ResourceInvalidException {
253
301
throw new ResourceInvalidException (
254
302
"Audience URL is empty. Metadata value must contain a valid URL." );
255
303
}
256
- return url ;
304
+ return new AudienceWrapper ( url ) ;
257
305
}
258
306
}
259
307
}
0 commit comments