• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #region Copyright notice and license
2 
3 // Copyright 2015-2016 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #endregion
18 
19 using System;
20 using System.Collections.Generic;
21 using System.IO;
22 using System.Linq;
23 using System.Threading;
24 using System.Threading.Tasks;
25 using Grpc.Core;
26 using Grpc.Core.Utils;
27 using Grpc.Testing;
28 using NUnit.Framework;
29 
30 namespace Grpc.IntegrationTesting
31 {
32     public class MetadataCredentialsTest
33     {
34         const string Host = "localhost";
35 
36         FakeTestService serviceImpl;
37         Server server;
38         Channel channel;
39         TestService.TestServiceClient client;
40         List<ChannelOption> options;
41 
42         [SetUp]
Init()43         public void Init()
44         {
45             serviceImpl = new FakeTestService();
46             // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
47             server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
48             {
49                 Services = { TestService.BindService(serviceImpl) },
50                 Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateSslServerCredentials() } }
51             };
52             server.Start();
53 
54             options = new List<ChannelOption>
55             {
56                 new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride)
57             };
58         }
59 
60         [TearDown]
Cleanup()61         public void Cleanup()
62         {
63             channel.ShutdownAsync().Wait();
64             server.ShutdownAsync().Wait();
65         }
66 
67         [Test]
MetadataCredentials_Channel()68         public void MetadataCredentials_Channel()
69         {
70             serviceImpl.UnaryCallHandler = (req, context) =>
71             {
72                 var authToken = context.RequestHeaders.First((entry) => entry.Key == "authorization").Value;
73                 Assert.AreEqual("SECRET_TOKEN", authToken);
74                 return Task.FromResult(new SimpleResponse());
75             };
76 
77             var asyncAuthInterceptor = new AsyncAuthInterceptor(async (context, metadata) =>
78             {
79                 await Task.Delay(100).ConfigureAwait(false);  // make sure the operation is asynchronous.
80                 metadata.Add("authorization", "SECRET_TOKEN");
81             });
82 
83             var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(),
84                 CallCredentials.FromInterceptor(asyncAuthInterceptor));
85             channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options);
86             client = new TestService.TestServiceClient(channel);
87 
88             client.UnaryCall(new SimpleRequest { });
89         }
90 
91         [Test]
MetadataCredentials_PerCall()92         public void MetadataCredentials_PerCall()
93         {
94             serviceImpl.UnaryCallHandler = (req, context) =>
95             {
96                 var authToken = context.RequestHeaders.First((entry) => entry.Key == "authorization").Value;
97                 Assert.AreEqual("SECRET_TOKEN", authToken);
98                 return Task.FromResult(new SimpleResponse());
99             };
100 
101             var asyncAuthInterceptor = new AsyncAuthInterceptor(async (context, metadata) =>
102             {
103                 await Task.Delay(100).ConfigureAwait(false);  // make sure the operation is asynchronous.
104                 metadata.Add("authorization", "SECRET_TOKEN");
105             });
106 
107             channel = new Channel(Host, server.Ports.Single().BoundPort, TestCredentials.CreateSslCredentials(), options);
108             client = new TestService.TestServiceClient(channel);
109 
110             var callCredentials = CallCredentials.FromInterceptor(asyncAuthInterceptor);
111             client.UnaryCall(new SimpleRequest { }, new CallOptions(credentials: callCredentials));
112         }
113 
114         [Test]
MetadataCredentials_BothChannelAndPerCall()115         public void MetadataCredentials_BothChannelAndPerCall()
116         {
117             serviceImpl.UnaryCallHandler = (req, context) =>
118             {
119                 var firstAuth = context.RequestHeaders.First((entry) => entry.Key == "first_authorization").Value;
120                 Assert.AreEqual("FIRST_SECRET_TOKEN", firstAuth);
121                 var secondAuth = context.RequestHeaders.First((entry) => entry.Key == "second_authorization").Value;
122                 Assert.AreEqual("SECOND_SECRET_TOKEN", secondAuth);
123                 // both values of "duplicate_authorization" are sent
124                 Assert.AreEqual("value1", context.RequestHeaders.First((entry) => entry.Key == "duplicate_authorization").Value);
125                 Assert.AreEqual("value2", context.RequestHeaders.Last((entry) => entry.Key == "duplicate_authorization").Value);
126                 return Task.FromResult(new SimpleResponse());
127             };
128 
129             var channelCallCredentials = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
130                 metadata.Add("first_authorization", "FIRST_SECRET_TOKEN");
131                 metadata.Add("duplicate_authorization", "value1");
132                 return TaskUtils.CompletedTask;
133             }));
134             var perCallCredentials = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
135                 metadata.Add("second_authorization", "SECOND_SECRET_TOKEN");
136                 metadata.Add("duplicate_authorization", "value2");
137                 return TaskUtils.CompletedTask;
138             }));
139 
140             var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(), channelCallCredentials);
141             channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options);
142             client = new TestService.TestServiceClient(channel);
143 
144             client.UnaryCall(new SimpleRequest { }, new CallOptions(credentials: perCallCredentials));
145         }
146 
147         [Test]
MetadataCredentials_Composed()148         public async Task MetadataCredentials_Composed()
149         {
150             serviceImpl.StreamingOutputCallHandler = async (req, responseStream, context) =>
151             {
152                 var firstAuth = context.RequestHeaders.Last((entry) => entry.Key == "first_authorization").Value;
153                 Assert.AreEqual("FIRST_SECRET_TOKEN", firstAuth);
154                 var secondAuth = context.RequestHeaders.First((entry) => entry.Key == "second_authorization").Value;
155                 Assert.AreEqual("SECOND_SECRET_TOKEN", secondAuth);
156                 var thirdAuth = context.RequestHeaders.First((entry) => entry.Key == "third_authorization").Value;
157                 Assert.AreEqual("THIRD_SECRET_TOKEN", thirdAuth);
158                 await responseStream.WriteAsync(new StreamingOutputCallResponse());
159             };
160 
161             var first = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
162                 // Attempt to exercise the case where async callback is inlineable/synchronously-runnable.
163                 metadata.Add("first_authorization", "FIRST_SECRET_TOKEN");
164                 return TaskUtils.CompletedTask;
165             }));
166             var second = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
167                 metadata.Add("second_authorization", "SECOND_SECRET_TOKEN");
168                 return TaskUtils.CompletedTask;
169             }));
170             var third = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
171                 metadata.Add("third_authorization", "THIRD_SECRET_TOKEN");
172                 return TaskUtils.CompletedTask;
173             }));
174             var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(),
175                 CallCredentials.Compose(first, second, third));
176             channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options);
177             var client = new TestService.TestServiceClient(channel);
178             var call = client.StreamingOutputCall(new StreamingOutputCallRequest { });
179             Assert.IsTrue(await call.ResponseStream.MoveNext());
180             Assert.IsFalse(await call.ResponseStream.MoveNext());
181         }
182 
183         [Test]
MetadataCredentials_ComposedPerCall()184         public async Task MetadataCredentials_ComposedPerCall()
185         {
186             serviceImpl.StreamingOutputCallHandler = async (req, responseStream, context) =>
187             {
188                 var firstAuth = context.RequestHeaders.Last((entry) => entry.Key == "first_authorization").Value;
189                 Assert.AreEqual("FIRST_SECRET_TOKEN", firstAuth);
190                 var secondAuth = context.RequestHeaders.First((entry) => entry.Key == "second_authorization").Value;
191                 Assert.AreEqual("SECOND_SECRET_TOKEN", secondAuth);
192                 await responseStream.WriteAsync(new StreamingOutputCallResponse());
193             };
194 
195             channel = new Channel(Host, server.Ports.Single().BoundPort, TestCredentials.CreateSslCredentials(), options);
196             var client = new TestService.TestServiceClient(channel);
197             var first = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
198                 metadata.Add("first_authorization", "FIRST_SECRET_TOKEN");
199                 return TaskUtils.CompletedTask;
200             }));
201             var second = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
202                 metadata.Add("second_authorization", "SECOND_SECRET_TOKEN");
203                 return TaskUtils.CompletedTask;
204             }));
205             var call = client.StreamingOutputCall(new StreamingOutputCallRequest{ },
206                 new CallOptions(credentials: CallCredentials.Compose(first, second)));
207             Assert.IsTrue(await call.ResponseStream.MoveNext());
208             Assert.IsFalse(await call.ResponseStream.MoveNext());
209         }
210 
211         [Test]
MetadataCredentials_InterceptorLeavesMetadataEmpty()212         public void MetadataCredentials_InterceptorLeavesMetadataEmpty()
213         {
214             serviceImpl.UnaryCallHandler = (req, context) =>
215             {
216                 var authHeaderCount = context.RequestHeaders.Count((entry) => entry.Key == "authorization");
217                 Assert.AreEqual(0, authHeaderCount);
218                 return Task.FromResult(new SimpleResponse());
219             };
220             var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(),
221                 CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => TaskUtils.CompletedTask)));
222             channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options);
223             client = new TestService.TestServiceClient(channel);
224             client.UnaryCall(new SimpleRequest { });
225         }
226 
227         [Test]
MetadataCredentials_InterceptorThrows()228         public void MetadataCredentials_InterceptorThrows()
229         {
230             var authInterceptorExceptionMessage = "Auth interceptor throws";
231             var callCredentials = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) =>
232             {
233                 throw new Exception(authInterceptorExceptionMessage);
234             }));
235             var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(), callCredentials);
236             channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options);
237             client = new TestService.TestServiceClient(channel);
238 
239             var ex = Assert.Throws<RpcException>(() => client.UnaryCall(new SimpleRequest { }));
240             Assert.AreEqual(StatusCode.Unavailable, ex.Status.StatusCode);
241             StringAssert.Contains(authInterceptorExceptionMessage, ex.Status.Detail);
242         }
243 
244         private class FakeTestService : TestService.TestServiceBase
245         {
246             public UnaryServerMethod<SimpleRequest, SimpleResponse> UnaryCallHandler;
247 
248             public ServerStreamingServerMethod<StreamingOutputCallRequest, StreamingOutputCallResponse> StreamingOutputCallHandler;
249 
UnaryCall(SimpleRequest request, ServerCallContext context)250             public override Task<SimpleResponse> UnaryCall(SimpleRequest request, ServerCallContext context)
251             {
252                 if (UnaryCallHandler != null)
253                 {
254                     return UnaryCallHandler(request, context);
255                 }
256                 return base.UnaryCall(request, context);
257             }
258 
StreamingOutputCall(StreamingOutputCallRequest request, IServerStreamWriter<StreamingOutputCallResponse> responseStream, ServerCallContext context)259             public override Task StreamingOutputCall(StreamingOutputCallRequest request, IServerStreamWriter<StreamingOutputCallResponse> responseStream, ServerCallContext context)
260             {
261                 if (StreamingOutputCallHandler != null)
262                 {
263                     return StreamingOutputCallHandler(request, responseStream, context);
264                 }
265                 return base.StreamingOutputCall(request, responseStream, context);
266             }
267         }
268     }
269 }
270