blob: c83ccd26128cd1d8b985384563494c9dcd41d7f1 [file] [log] [blame]
Jan Tattermusch5bd75d72015-09-08 10:55:20 -07001#region Copyright notice and license
2
Jan Tattermusch7897ae92017-06-07 22:57:36 +02003// Copyright 2015-2016 gRPC authors.
Jan Tattermusch5bd75d72015-09-08 10:55:20 -07004//
Jan Tattermusch7897ae92017-06-07 22:57:36 +02005// 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
Jan Tattermusch5bd75d72015-09-08 10:55:20 -07008//
Jan Tattermusch7897ae92017-06-07 22:57:36 +02009// http://www.apache.org/licenses/LICENSE-2.0
Jan Tattermusch5bd75d72015-09-08 10:55:20 -070010//
Jan Tattermusch7897ae92017-06-07 22:57:36 +020011// 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.
Jan Tattermusch5bd75d72015-09-08 10:55:20 -070016
17#endregion
18
19using System;
20using System.Collections.Generic;
21using System.IO;
22using System.Linq;
23using System.Threading;
24using System.Threading.Tasks;
25using Grpc.Core;
26using Grpc.Core.Utils;
27using Grpc.Testing;
28using NUnit.Framework;
29
30namespace Grpc.IntegrationTesting
31{
32 public class MetadataCredentialsTest
33 {
34 const string Host = "localhost";
35 Server server;
36 Channel channel;
Jan Tattermuschd8f7c8a2016-03-21 19:02:58 -070037 TestService.TestServiceClient client;
Jan Tattermusch3ac49002015-12-09 16:48:16 -080038 List<ChannelOption> options;
Jan Tattermusch3ac49002015-12-09 16:48:16 -080039 AsyncAuthInterceptor asyncAuthInterceptor;
Jan Tattermusch5bd75d72015-09-08 10:55:20 -070040
Jan Tattermusch3ac49002015-12-09 16:48:16 -080041 [SetUp]
Jan Tattermusch5bd75d72015-09-08 10:55:20 -070042 public void Init()
43 {
Jan Tattermusch09d2f552017-04-20 11:39:37 +020044 // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
45 server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
Jan Tattermusch5bd75d72015-09-08 10:55:20 -070046 {
Jan Tattermusch317b8ac2016-06-16 09:36:11 -070047 Services = { TestService.BindService(new FakeTestService()) },
Jan Tattermusch3ac49002015-12-09 16:48:16 -080048 Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateSslServerCredentials() } }
Jan Tattermusch5bd75d72015-09-08 10:55:20 -070049 };
50 server.Start();
51
Jan Tattermusch3ac49002015-12-09 16:48:16 -080052 options = new List<ChannelOption>
Jan Tattermusch5bd75d72015-09-08 10:55:20 -070053 {
54 new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride)
55 };
56
Jan Tattermusch3ac49002015-12-09 16:48:16 -080057 asyncAuthInterceptor = new AsyncAuthInterceptor(async (context, metadata) =>
Jan Tattermusch5bd75d72015-09-08 10:55:20 -070058 {
Jan Tattermusch3ac49002015-12-09 16:48:16 -080059 await Task.Delay(100).ConfigureAwait(false); // make sure the operation is asynchronous.
Jan Tattermusch5bd75d72015-09-08 10:55:20 -070060 metadata.Add("authorization", "SECRET_TOKEN");
61 });
Jan Tattermusch5bd75d72015-09-08 10:55:20 -070062 }
63
Jan Tattermusch3ac49002015-12-09 16:48:16 -080064 [TearDown]
Jan Tattermusch5bd75d72015-09-08 10:55:20 -070065 public void Cleanup()
66 {
67 channel.ShutdownAsync().Wait();
68 server.ShutdownAsync().Wait();
69 }
70
71 [Test]
72 public void MetadataCredentials()
73 {
Jan Tattermusch3ac49002015-12-09 16:48:16 -080074 var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(),
75 CallCredentials.FromInterceptor(asyncAuthInterceptor));
76 channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options);
Jan Tattermuschf41ebc32016-06-22 12:47:14 -070077 client = new TestService.TestServiceClient(channel);
Jan Tattermusch3ac49002015-12-09 16:48:16 -080078
Jan Tattermusch317b8ac2016-06-16 09:36:11 -070079 client.UnaryCall(new SimpleRequest { });
Jan Tattermusch3ac49002015-12-09 16:48:16 -080080 }
81
82 [Test]
83 public void MetadataCredentials_PerCall()
84 {
85 channel = new Channel(Host, server.Ports.Single().BoundPort, TestCredentials.CreateSslCredentials(), options);
Jan Tattermuschf41ebc32016-06-22 12:47:14 -070086 client = new TestService.TestServiceClient(channel);
Jan Tattermusch3ac49002015-12-09 16:48:16 -080087
88 var callCredentials = CallCredentials.FromInterceptor(asyncAuthInterceptor);
89 client.UnaryCall(new SimpleRequest { }, new CallOptions(credentials: callCredentials));
90 }
91
Jan Tattermusch90eb3882016-11-09 22:53:16 +010092 [Test]
Alexander Polcyn27670c82017-09-05 20:39:28 -070093 public async Task MetadataCredentials_Composed()
94 {
95 var first = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
96 // Attempt to exercise the case where async callback is inlineable/synchronously-runnable.
97 metadata.Add("first_authorization", "FIRST_SECRET_TOKEN");
98 return TaskUtils.CompletedTask;
99 }));
100 var second = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
101 metadata.Add("second_authorization", "SECOND_SECRET_TOKEN");
102 return TaskUtils.CompletedTask;
103 }));
104 var third = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
105 metadata.Add("third_authorization", "THIRD_SECRET_TOKEN");
106 return TaskUtils.CompletedTask;
107 }));
108 var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(),
109 CallCredentials.Compose(first, second, third));
110 channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options);
111 var client = new TestService.TestServiceClient(channel);
112 var call = client.StreamingOutputCall(new StreamingOutputCallRequest { });
113 Assert.IsTrue(await call.ResponseStream.MoveNext());
114 Assert.IsFalse(await call.ResponseStream.MoveNext());
115 }
116
117 [Test]
118 public async Task MetadataCredentials_ComposedPerCall()
119 {
120 channel = new Channel(Host, server.Ports.Single().BoundPort, TestCredentials.CreateSslCredentials(), options);
121 var client = new TestService.TestServiceClient(channel);
122 var first = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
123 metadata.Add("first_authorization", "FIRST_SECRET_TOKEN");
124 return TaskUtils.CompletedTask;
125 }));
126 var second = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
127 metadata.Add("second_authorization", "SECOND_SECRET_TOKEN");
128 return TaskUtils.CompletedTask;
129 }));
130 var third = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
131 metadata.Add("third_authorization", "THIRD_SECRET_TOKEN");
132 return TaskUtils.CompletedTask;
133 }));
134 var call = client.StreamingOutputCall(new StreamingOutputCallRequest{ },
135 new CallOptions(credentials: CallCredentials.Compose(first, second, third)));
136 Assert.IsTrue(await call.ResponseStream.MoveNext());
137 Assert.IsFalse(await call.ResponseStream.MoveNext());
138 }
139
140 [Test]
Jan Tattermusch90eb3882016-11-09 22:53:16 +0100141 public void MetadataCredentials_InterceptorLeavesMetadataEmpty()
142 {
143 var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(),
144 CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => TaskUtils.CompletedTask)));
145 channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options);
146 client = new TestService.TestServiceClient(channel);
147
148 var ex = Assert.Throws<RpcException>(() => client.UnaryCall(new SimpleRequest { }));
Jan Tattermusch60d0f4c2016-11-10 09:54:33 +0100149 // StatusCode.Unknown as the server-side handler throws an exception after not receiving the authorization header.
Jan Tattermusch90eb3882016-11-09 22:53:16 +0100150 Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode);
151 }
152
153 [Test]
154 public void MetadataCredentials_InterceptorThrows()
155 {
156 var callCredentials = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) =>
157 {
158 throw new Exception("Auth interceptor throws");
159 }));
160 var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(), callCredentials);
161 channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options);
162 client = new TestService.TestServiceClient(channel);
163
164 var ex = Assert.Throws<RpcException>(() => client.UnaryCall(new SimpleRequest { }));
Jan Tattermuscha2044f82017-11-13 08:05:34 +0100165 Assert.AreEqual(StatusCode.Unavailable, ex.Status.StatusCode);
Jan Tattermusch90eb3882016-11-09 22:53:16 +0100166 }
167
Jan Tattermusch317b8ac2016-06-16 09:36:11 -0700168 private class FakeTestService : TestService.TestServiceBase
Jan Tattermusch3ac49002015-12-09 16:48:16 -0800169 {
Jan Tattermusch317b8ac2016-06-16 09:36:11 -0700170 public override Task<SimpleResponse> UnaryCall(SimpleRequest request, ServerCallContext context)
171 {
172 var authToken = context.RequestHeaders.First((entry) => entry.Key == "authorization").Value;
173 Assert.AreEqual("SECRET_TOKEN", authToken);
174 return Task.FromResult(new SimpleResponse());
175 }
Alexander Polcyn27670c82017-09-05 20:39:28 -0700176
177 public override async Task StreamingOutputCall(StreamingOutputCallRequest request, IServerStreamWriter<StreamingOutputCallResponse> responseStream, ServerCallContext context)
178 {
179 var first = context.RequestHeaders.First((entry) => entry.Key == "first_authorization").Value;
180 Assert.AreEqual("FIRST_SECRET_TOKEN", first);
181 var second = context.RequestHeaders.First((entry) => entry.Key == "second_authorization").Value;
182 Assert.AreEqual("SECOND_SECRET_TOKEN", second);
183 var third = context.RequestHeaders.First((entry) => entry.Key == "third_authorization").Value;
184 Assert.AreEqual("THIRD_SECRET_TOKEN", third);
185 await responseStream.WriteAsync(new StreamingOutputCallResponse());
186 }
Jan Tattermusch5bd75d72015-09-08 10:55:20 -0700187 }
188 }
189}