Merge pull request #2895 from jtattermusch/switch_to_usable_auth

migrate to usable auth library
pull/2612/head
Jan Tattermusch 10 years ago
commit 3b226512d4
  1. 125
      src/csharp/Grpc.Auth/GoogleCredential.cs
  2. 27
      src/csharp/Grpc.Auth/Grpc.Auth.csproj
  3. 26
      src/csharp/Grpc.Auth/OAuth2Interceptors.cs
  4. 4
      src/csharp/Grpc.Auth/app.config
  5. 4
      src/csharp/Grpc.Auth/packages.config
  6. 4
      src/csharp/Grpc.Core.Tests/ChannelTest.cs
  7. 24
      src/csharp/Grpc.Core/Channel.cs
  8. 11
      src/csharp/Grpc.Core/ClientBase.cs
  9. 4
      src/csharp/Grpc.IntegrationTesting.Client/app.config
  10. 4
      src/csharp/Grpc.IntegrationTesting.Server/app.config
  11. 38
      src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
  12. 80
      src/csharp/Grpc.IntegrationTesting/InteropClient.cs
  13. 4
      src/csharp/Grpc.IntegrationTesting/app.config
  14. 4
      src/csharp/Grpc.IntegrationTesting/packages.config

@ -1,125 +0,0 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Responses;
using Newtonsoft.Json.Linq;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
namespace Grpc.Auth
{
// TODO(jtattermusch): Remove this class once possible.
/// <summary>
/// A temporary placeholder for Google credential from
/// Google Auth library for .NET. It emulates the usage pattern
/// for Usable auth.
/// </summary>
public class GoogleCredential
{
private const string GoogleApplicationCredentialsEnvName = "GOOGLE_APPLICATION_CREDENTIALS";
private const string ClientEmailFieldName = "client_email";
private const string PrivateKeyFieldName = "private_key";
private ServiceCredential credential;
private GoogleCredential(ServiceCredential credential)
{
this.credential = credential;
}
public static GoogleCredential GetApplicationDefault()
{
return new GoogleCredential(null);
}
public bool IsCreateScopedRequired
{
get
{
return true;
}
}
public GoogleCredential CreateScoped(IEnumerable<string> scopes)
{
var credsPath = Environment.GetEnvironmentVariable(GoogleApplicationCredentialsEnvName);
if (credsPath == null)
{
// Default to ComputeCredentials if path to JSON key is not set.
// ComputeCredential is not scoped actually, but for our use case it's
// fine to treat is as such.
return new GoogleCredential(new ComputeCredential(new ComputeCredential.Initializer()));
}
JObject jsonCredentialParameters = JObject.Parse(File.ReadAllText(credsPath));
string clientEmail = jsonCredentialParameters.GetValue(ClientEmailFieldName).Value<string>();
string privateKeyString = jsonCredentialParameters.GetValue(PrivateKeyFieldName).Value<string>();
var serviceCredential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(clientEmail)
{
Scopes = scopes,
}.FromPrivateKey(privateKeyString));
return new GoogleCredential(serviceCredential);
}
public Task<bool> RequestAccessTokenAsync(CancellationToken taskCancellationToken)
{
return credential.RequestAccessTokenAsync(taskCancellationToken);
}
public TokenResponse Token
{
get
{
return credential.Token;
}
}
internal ServiceCredential InternalCredential
{
get
{
return credential;
}
}
}
}

@ -11,7 +11,7 @@
<AssemblyName>Grpc.Auth</AssemblyName> <AssemblyName>Grpc.Auth</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<DocumentationFile>bin\$(Configuration)\Grpc.Auth.Xml</DocumentationFile> <DocumentationFile>bin\$(Configuration)\Grpc.Auth.Xml</DocumentationFile>
<NuGetPackageImportStamp>9b408026</NuGetPackageImportStamp> <NuGetPackageImportStamp>4f8487a9</NuGetPackageImportStamp>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -41,28 +41,32 @@
<AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="BouncyCastle.Crypto"> <Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath> <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
</Reference> </Reference>
<Reference Include="Google.Apis.Auth, Version=1.9.2.27817, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.dll</HintPath> <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
</Reference> </Reference>
<Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.2.27820, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath> <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
</Reference> </Reference>
<Reference Include="Google.Apis.Core, Version=1.9.2.27816, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Core.1.9.2\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath> <HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Threading.Tasks"> <Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions"> <Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop"> <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
@ -87,7 +91,6 @@
<Link>Version.cs</Link> <Link>Version.cs</Link>
</Compile> </Compile>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="GoogleCredential.cs" />
<Compile Include="OAuth2Interceptors.cs" /> <Compile Include="OAuth2Interceptors.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />

@ -54,7 +54,7 @@ namespace Grpc.Auth
/// </summary> /// </summary>
public static MetadataInterceptorDelegate FromCredential(GoogleCredential googleCredential) public static MetadataInterceptorDelegate FromCredential(GoogleCredential googleCredential)
{ {
var interceptor = new OAuth2Interceptor(googleCredential.InternalCredential, SystemClock.Default); var interceptor = new OAuth2Interceptor(googleCredential, SystemClock.Default);
return new MetadataInterceptorDelegate(interceptor.InterceptHeaders); return new MetadataInterceptorDelegate(interceptor.InterceptHeaders);
} }
@ -66,7 +66,7 @@ namespace Grpc.Auth
public static MetadataInterceptorDelegate FromAccessToken(string oauth2Token) public static MetadataInterceptorDelegate FromAccessToken(string oauth2Token)
{ {
Preconditions.CheckNotNull(oauth2Token); Preconditions.CheckNotNull(oauth2Token);
return new MetadataInterceptorDelegate((metadata) => return new MetadataInterceptorDelegate((authUri, metadata) =>
{ {
metadata.Add(OAuth2Interceptor.CreateBearerTokenHeader(oauth2Token)); metadata.Add(OAuth2Interceptor.CreateBearerTokenHeader(oauth2Token));
}); });
@ -80,10 +80,10 @@ namespace Grpc.Auth
private const string AuthorizationHeader = "Authorization"; private const string AuthorizationHeader = "Authorization";
private const string Schema = "Bearer"; private const string Schema = "Bearer";
private ServiceCredential credential; private ITokenAccess credential;
private IClock clock; private IClock clock;
public OAuth2Interceptor(ServiceCredential credential, IClock clock) public OAuth2Interceptor(ITokenAccess credential, IClock clock)
{ {
this.credential = credential; this.credential = credential;
this.clock = clock; this.clock = clock;
@ -94,23 +94,15 @@ namespace Grpc.Auth
/// </summary> /// </summary>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
public string GetAccessToken(CancellationToken cancellationToken) public string GetAccessToken(string authUri, CancellationToken cancellationToken)
{ {
if (credential.Token == null || credential.Token.IsExpired(clock)) // TODO(jtattermusch): Rethink synchronous wait to obtain the result.
{ return credential.GetAccessTokenForRequestAsync(authUri, cancellationToken: cancellationToken).GetAwaiter().GetResult();
// TODO(jtattermusch): Parallel requests will spawn multiple requests to refresh the token once the token expires.
// TODO(jtattermusch): Rethink synchronous wait to obtain the result.
if (!credential.RequestAccessTokenAsync(cancellationToken).Result)
{
throw new InvalidOperationException("The access token has expired but we can't refresh it");
}
}
return credential.Token.AccessToken;
} }
public void InterceptHeaders(Metadata metadata) public void InterceptHeaders(string authUri, Metadata metadata)
{ {
var accessToken = GetAccessToken(CancellationToken.None); var accessToken = GetAccessToken(authUri, CancellationToken.None);
metadata.Add(CreateBearerTokenHeader(accessToken)); metadata.Add(CreateBearerTokenHeader(accessToken));
} }

@ -10,6 +10,10 @@
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.9.2.38523" newVersion="1.9.2.38523" />
</dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
</configuration> </configuration>

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="BouncyCastle" version="1.7.0" targetFramework="net45" /> <package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
<package id="Google.Apis.Auth" version="1.9.2" targetFramework="net45" /> <package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" />
<package id="Google.Apis.Core" version="1.9.2" targetFramework="net45" /> <package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" />
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" /> <package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" /> <package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" />
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" /> <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />

@ -72,11 +72,11 @@ namespace Grpc.Core.Tests
} }
[Test] [Test]
public void Target() public void ResolvedTarget()
{ {
using (var channel = new Channel("127.0.0.1", Credentials.Insecure)) using (var channel = new Channel("127.0.0.1", Credentials.Insecure))
{ {
Assert.IsTrue(channel.Target.Contains("127.0.0.1")); Assert.IsTrue(channel.ResolvedTarget.Contains("127.0.0.1"));
} }
} }

@ -49,6 +49,7 @@ namespace Grpc.Core
{ {
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<Channel>(); static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<Channel>();
readonly string target;
readonly GrpcEnvironment environment; readonly GrpcEnvironment environment;
readonly ChannelSafeHandle handle; readonly ChannelSafeHandle handle;
readonly List<ChannelOption> options; readonly List<ChannelOption> options;
@ -58,12 +59,12 @@ namespace Grpc.Core
/// Creates a channel that connects to a specific host. /// Creates a channel that connects to a specific host.
/// Port will default to 80 for an unsecure channel and to 443 for a secure channel. /// Port will default to 80 for an unsecure channel and to 443 for a secure channel.
/// </summary> /// </summary>
/// <param name="host">The name or IP address of the host.</param> /// <param name="target">Target of the channel.</param>
/// <param name="credentials">Credentials to secure the channel.</param> /// <param name="credentials">Credentials to secure the channel.</param>
/// <param name="options">Channel options.</param> /// <param name="options">Channel options.</param>
public Channel(string host, Credentials credentials, IEnumerable<ChannelOption> options = null) public Channel(string target, Credentials credentials, IEnumerable<ChannelOption> options = null)
{ {
Preconditions.CheckNotNull(host, "host"); this.target = Preconditions.CheckNotNull(target, "target");
this.environment = GrpcEnvironment.GetInstance(); this.environment = GrpcEnvironment.GetInstance();
this.options = options != null ? new List<ChannelOption>(options) : new List<ChannelOption>(); this.options = options != null ? new List<ChannelOption>(options) : new List<ChannelOption>();
@ -73,11 +74,11 @@ namespace Grpc.Core
{ {
if (nativeCredentials != null) if (nativeCredentials != null)
{ {
this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, host, nativeChannelArgs); this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, target, nativeChannelArgs);
} }
else else
{ {
this.handle = ChannelSafeHandle.CreateInsecure(host, nativeChannelArgs); this.handle = ChannelSafeHandle.CreateInsecure(target, nativeChannelArgs);
} }
} }
} }
@ -131,8 +132,8 @@ namespace Grpc.Core
return tcs.Task; return tcs.Task;
} }
/// <summary> Address of the remote endpoint in URI format.</summary> /// <summary>Resolved address of the remote endpoint in URI format.</summary>
public string Target public string ResolvedTarget
{ {
get get
{ {
@ -140,6 +141,15 @@ namespace Grpc.Core
} }
} }
/// <summary>The original target used to create the channel.</summary>
public string Target
{
get
{
return this.target;
}
}
/// <summary> /// <summary>
/// Allows explicitly requesting channel to connect without starting an RPC. /// Allows explicitly requesting channel to connect without starting an RPC.
/// Returned task completes once state Ready was seen. If the deadline is reached, /// Returned task completes once state Ready was seen. If the deadline is reached,

@ -35,21 +35,26 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Grpc.Core.Internal; using Grpc.Core.Internal;
using System.Text.RegularExpressions;
namespace Grpc.Core namespace Grpc.Core
{ {
public delegate void MetadataInterceptorDelegate(Metadata metadata); public delegate void MetadataInterceptorDelegate(string authUri, Metadata metadata);
/// <summary> /// <summary>
/// Base class for client-side stubs. /// Base class for client-side stubs.
/// </summary> /// </summary>
public abstract class ClientBase public abstract class ClientBase
{ {
static readonly Regex TrailingPortPattern = new Regex(":[0-9]+/?$");
readonly Channel channel; readonly Channel channel;
readonly string authUriBase;
public ClientBase(Channel channel) public ClientBase(Channel channel)
{ {
this.channel = channel; this.channel = channel;
// TODO(jtattermush): we shouldn't need to hand-curate the channel.Target contents.
this.authUriBase = "https://" + TrailingPortPattern.Replace(channel.Target, "") + "/";
} }
/// <summary> /// <summary>
@ -99,8 +104,8 @@ namespace Grpc.Core
{ {
options = options.WithHeaders(new Metadata()); options = options.WithHeaders(new Metadata());
} }
interceptor(options.Headers); var authUri = authUriBase + method.ServiceName;
options.Headers.Freeze(); interceptor(authUri, options.Headers);
} }
return new CallInvocationDetails<TRequest, TResponse>(channel, method, Host, options); return new CallInvocationDetails<TRequest, TResponse>(channel, method, Host, options);
} }

@ -10,6 +10,10 @@
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.9.2.38523" newVersion="1.9.2.38523" />
</dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
</configuration> </configuration>

@ -10,6 +10,10 @@
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.9.2.38523" newVersion="1.9.2.38523" />
</dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
</configuration> </configuration>

@ -8,7 +8,7 @@
<RootNamespace>Grpc.IntegrationTesting</RootNamespace> <RootNamespace>Grpc.IntegrationTesting</RootNamespace>
<AssemblyName>Grpc.IntegrationTesting</AssemblyName> <AssemblyName>Grpc.IntegrationTesting</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<NuGetPackageImportStamp>041c163e</NuGetPackageImportStamp> <NuGetPackageImportStamp>6566287f</NuGetPackageImportStamp>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -38,20 +38,33 @@
<AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="BouncyCastle.Crypto"> <Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath> <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
</Reference> </Reference>
<Reference Include="Google.Apis.Auth, Version=1.9.2.27817, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
</Reference> </Reference>
<Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.2.27820, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Google.Apis.Core, Version=1.9.2.27816, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Core.1.9.2\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
@ -78,15 +91,6 @@
<HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath> <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Net.Http.WebRequest" /> <Reference Include="System.Net.Http.WebRequest" />
<Reference Include="Microsoft.Threading.Tasks">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\Grpc.Core\Version.cs"> <Compile Include="..\Grpc.Core\Version.cs">

@ -43,6 +43,7 @@ using Grpc.Auth;
using Grpc.Core; using Grpc.Core;
using Grpc.Core.Utils; using Grpc.Core.Utils;
using NUnit.Framework; using NUnit.Framework;
using Google.Apis.Auth.OAuth2;
namespace Grpc.IntegrationTesting namespace Grpc.IntegrationTesting
{ {
@ -97,10 +98,10 @@ namespace Grpc.IntegrationTesting
} }
var interopClient = new InteropClient(options); var interopClient = new InteropClient(options);
interopClient.Run(); interopClient.Run().Wait();
} }
private void Run() private async Task Run()
{ {
Credentials credentials = null; Credentials credentials = null;
if (options.useTls) if (options.useTls)
@ -120,17 +121,7 @@ namespace Grpc.IntegrationTesting
using (Channel channel = new Channel(options.serverHost, options.serverPort.Value, credentials, channelOptions)) using (Channel channel = new Channel(options.serverHost, options.serverPort.Value, credentials, channelOptions))
{ {
TestService.TestServiceClient client = new TestService.TestServiceClient(channel); TestService.TestServiceClient client = new TestService.TestServiceClient(channel);
if (options.testCase == "service_account_creds" || options.testCase == "compute_engine_creds") await RunTestCaseAsync(options.testCase, client);
{
var credential = GoogleCredential.GetApplicationDefault();
if (credential.IsCreateScopedRequired)
{
credential = credential.CreateScoped(new[] { AuthScope });
}
client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential);
}
RunTestCaseAsync(options.testCase, client).Wait();
} }
GrpcEnvironment.Shutdown(); GrpcEnvironment.Shutdown();
} }
@ -158,16 +149,19 @@ namespace Grpc.IntegrationTesting
await RunEmptyStreamAsync(client); await RunEmptyStreamAsync(client);
break; break;
case "service_account_creds": case "service_account_creds":
RunServiceAccountCreds(client); await RunServiceAccountCredsAsync(client);
break; break;
case "compute_engine_creds": case "compute_engine_creds":
RunComputeEngineCreds(client); await RunComputeEngineCredsAsync(client);
break;
case "jwt_token_creds":
await RunJwtTokenCredsAsync(client);
break; break;
case "oauth2_auth_token": case "oauth2_auth_token":
RunOAuth2AuthToken(client); await RunOAuth2AuthTokenAsync(client);
break; break;
case "per_rpc_creds": case "per_rpc_creds":
RunPerRpcCreds(client); await RunPerRpcCredsAsync(client);
break; break;
case "cancel_after_begin": case "cancel_after_begin":
await RunCancelAfterBeginAsync(client); await RunCancelAfterBeginAsync(client);
@ -309,9 +303,13 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("Passed!"); Console.WriteLine("Passed!");
} }
public static void RunServiceAccountCreds(TestService.ITestServiceClient client) public static async Task RunServiceAccountCredsAsync(TestService.TestServiceClient client)
{ {
Console.WriteLine("running service_account_creds"); Console.WriteLine("running service_account_creds");
var credential = await GoogleCredential.GetApplicationDefaultAsync();
credential = credential.CreateScoped(new[] { AuthScope });
client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential);
var request = SimpleRequest.CreateBuilder() var request = SimpleRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE) .SetResponseType(PayloadType.COMPRESSABLE)
.SetResponseSize(314159) .SetResponseSize(314159)
@ -329,9 +327,13 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("Passed!"); Console.WriteLine("Passed!");
} }
public static void RunComputeEngineCreds(TestService.ITestServiceClient client) public static async Task RunComputeEngineCredsAsync(TestService.TestServiceClient client)
{ {
Console.WriteLine("running compute_engine_creds"); Console.WriteLine("running compute_engine_creds");
var credential = await GoogleCredential.GetApplicationDefaultAsync();
Assert.IsFalse(credential.IsCreateScopedRequired);
client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential);
var request = SimpleRequest.CreateBuilder() var request = SimpleRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE) .SetResponseType(PayloadType.COMPRESSABLE)
.SetResponseSize(314159) .SetResponseSize(314159)
@ -349,12 +351,35 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("Passed!"); Console.WriteLine("Passed!");
} }
public static void RunOAuth2AuthToken(TestService.TestServiceClient client) public static async Task RunJwtTokenCredsAsync(TestService.TestServiceClient client)
{
Console.WriteLine("running jwt_token_creds");
var credential = await GoogleCredential.GetApplicationDefaultAsync();
// check this a credential with scope support, but don't add the scope.
Assert.IsTrue(credential.IsCreateScopedRequired);
client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential);
var request = SimpleRequest.CreateBuilder()
.SetResponseType(PayloadType.COMPRESSABLE)
.SetResponseSize(314159)
.SetPayload(CreateZerosPayload(271828))
.SetFillUsername(true)
.SetFillOauthScope(true)
.Build();
var response = client.UnaryCall(request);
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
Assert.AreEqual(314159, response.Payload.Body.Length);
Assert.AreEqual(ServiceAccountUser, response.Username);
Console.WriteLine("Passed!");
}
public static async Task RunOAuth2AuthTokenAsync(TestService.TestServiceClient client)
{ {
Console.WriteLine("running oauth2_auth_token"); Console.WriteLine("running oauth2_auth_token");
var credential = GoogleCredential.GetApplicationDefault().CreateScoped(new[] { AuthScope }); ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { AuthScope });
Assert.IsTrue(credential.RequestAccessTokenAsync(CancellationToken.None).Result); string oauth2Token = await credential.GetAccessTokenForRequestAsync();
string oauth2Token = credential.Token.AccessToken;
client.HeaderInterceptor = OAuth2Interceptors.FromAccessToken(oauth2Token); client.HeaderInterceptor = OAuth2Interceptors.FromAccessToken(oauth2Token);
@ -370,13 +395,12 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("Passed!"); Console.WriteLine("Passed!");
} }
public static void RunPerRpcCreds(TestService.TestServiceClient client) public static async Task RunPerRpcCredsAsync(TestService.TestServiceClient client)
{ {
Console.WriteLine("running per_rpc_creds"); Console.WriteLine("running per_rpc_creds");
var credential = GoogleCredential.GetApplicationDefault().CreateScoped(new[] { AuthScope }); ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { AuthScope });
Assert.IsTrue(credential.RequestAccessTokenAsync(CancellationToken.None).Result); string oauth2Token = await credential.GetAccessTokenForRequestAsync();
string oauth2Token = credential.Token.AccessToken;
var headerInterceptor = OAuth2Interceptors.FromAccessToken(oauth2Token); var headerInterceptor = OAuth2Interceptors.FromAccessToken(oauth2Token);
var request = SimpleRequest.CreateBuilder() var request = SimpleRequest.CreateBuilder()
@ -385,7 +409,7 @@ namespace Grpc.IntegrationTesting
.Build(); .Build();
var headers = new Metadata(); var headers = new Metadata();
headerInterceptor(headers); headerInterceptor("", headers);
var response = client.UnaryCall(request, headers: headers); var response = client.UnaryCall(request, headers: headers);
Assert.AreEqual(AuthScopeResponse, response.OauthScope); Assert.AreEqual(AuthScopeResponse, response.OauthScope);

@ -10,6 +10,10 @@
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.9.2.38523" newVersion="1.9.2.38523" />
</dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
</configuration> </configuration>

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="BouncyCastle" version="1.7.0" targetFramework="net45" /> <package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
<package id="Google.Apis.Auth" version="1.9.2" targetFramework="net45" /> <package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" />
<package id="Google.Apis.Core" version="1.9.2" targetFramework="net45" /> <package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" />
<package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" /> <package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" />
<package id="Ix-Async" version="1.2.3" targetFramework="net45" /> <package id="Ix-Async" version="1.2.3" targetFramework="net45" />
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" /> <package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />

Loading…
Cancel
Save