diff --git a/src/csharp/Grpc.Core.Tests/FakeCredentials.cs b/src/csharp/Grpc.Core.Tests/FakeCredentials.cs index f23c9e97574..59587b9a510 100644 --- a/src/csharp/Grpc.Core.Tests/FakeCredentials.cs +++ b/src/csharp/Grpc.Core.Tests/FakeCredentials.cs @@ -42,9 +42,9 @@ namespace Grpc.Core.Tests internal class FakeCallCredentials : CallCredentials { - internal override CallCredentialsSafeHandle ToNativeCredentials() + public override void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object state) { - return null; + // not invoking the configurator on purpose } } } diff --git a/src/csharp/Grpc.Core/CallCredentials.cs b/src/csharp/Grpc.Core/CallCredentials.cs index c1bd95b9e22..6344a881070 100644 --- a/src/csharp/Grpc.Core/CallCredentials.cs +++ b/src/csharp/Grpc.Core/CallCredentials.cs @@ -16,9 +16,8 @@ #endregion -using System; using System.Collections.Generic; -using System.Threading.Tasks; +using System.Collections.ObjectModel; using Grpc.Core.Internal; using Grpc.Core.Utils; @@ -48,81 +47,43 @@ namespace Grpc.Core /// authentication interceptor public static CallCredentials FromInterceptor(AsyncAuthInterceptor interceptor) { - return new MetadataCredentials(interceptor); + return new AsyncAuthInterceptorCredentials(interceptor); } /// - /// Creates native object for the credentials. + /// Populates this call credential instances. + /// You never need to invoke this, part of internal implementation. /// - /// The native credentials. - internal abstract CallCredentialsSafeHandle ToNativeCredentials(); - } + public abstract void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object state); - /// - /// Client-side credentials that delegate metadata based auth to an interceptor. - /// The interceptor is automatically invoked for each remote call that uses MetadataCredentials. - /// - internal sealed class MetadataCredentials : CallCredentials - { - readonly AsyncAuthInterceptor interceptor; - - /// - /// Initializes a new instance of MetadataCredentials class. - /// - /// authentication interceptor - public MetadataCredentials(AsyncAuthInterceptor interceptor) - { - this.interceptor = GrpcPreconditions.CheckNotNull(interceptor); - } - - internal override CallCredentialsSafeHandle ToNativeCredentials() + private class CompositeCallCredentials : CallCredentials { - NativeMetadataCredentialsPlugin plugin = new NativeMetadataCredentialsPlugin(interceptor); - return plugin.Credentials; - } - } + readonly IReadOnlyList credentials; - /// - /// Credentials that allow composing multiple credentials objects into one object. - /// - internal sealed class CompositeCallCredentials : CallCredentials - { - readonly List credentials; + public CompositeCallCredentials(CallCredentials[] credentials) + { + GrpcPreconditions.CheckArgument(credentials.Length >= 2, "Composite credentials object can only be created from 2 or more credentials."); + this.credentials = new List(credentials).AsReadOnly(); + } - /// - /// Initializes a new instance of CompositeCallCredentials class. - /// The resulting credentials object will be composite of all the credentials specified as parameters. - /// - /// credentials to compose - public CompositeCallCredentials(params CallCredentials[] credentials) - { - GrpcPreconditions.CheckArgument(credentials.Length >= 2, "Composite credentials object can only be created from 2 or more credentials."); - this.credentials = new List(credentials); + public override void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object state) + { + configurator.SetCompositeCredentials(state, credentials); + } } - internal override CallCredentialsSafeHandle ToNativeCredentials() + private class AsyncAuthInterceptorCredentials : CallCredentials { - return ToNativeRecursive(0); - } + readonly AsyncAuthInterceptor interceptor; - // Recursive descent makes managing lifetime of intermediate CredentialSafeHandle instances easier. - // In practice, we won't usually see composites from more than two credentials anyway. - private CallCredentialsSafeHandle ToNativeRecursive(int startIndex) - { - if (startIndex == credentials.Count - 1) + public AsyncAuthInterceptorCredentials(AsyncAuthInterceptor interceptor) { - return credentials[startIndex].ToNativeCredentials(); + this.interceptor = GrpcPreconditions.CheckNotNull(interceptor); } - using (var cred1 = credentials[startIndex].ToNativeCredentials()) - using (var cred2 = ToNativeRecursive(startIndex + 1)) + public override void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object state) { - var nativeComposite = CallCredentialsSafeHandle.CreateComposite(cred1, cred2); - if (nativeComposite.IsInvalid) - { - throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials."); - } - return nativeComposite; + configurator.SetAsyncAuthInterceptorCredentials(state, interceptor); } } } diff --git a/src/csharp/Grpc.Core/CallCredentialsConfiguratorBase.cs b/src/csharp/Grpc.Core/CallCredentialsConfiguratorBase.cs new file mode 100644 index 00000000000..40a33bbf599 --- /dev/null +++ b/src/csharp/Grpc.Core/CallCredentialsConfiguratorBase.cs @@ -0,0 +1,38 @@ +#region Copyright notice and license + +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#endregion + +using System.Collections.Generic; + +namespace Grpc.Core +{ + /// + /// Base class for objects that can consume configuration from CallCredentials objects. + /// + public abstract class CallCredentialsConfiguratorBase + { + /// + /// Consumes configuration for composite call credentials. + /// + public abstract void SetCompositeCredentials(object state, IReadOnlyList credentials); + + /// + /// Consumes configuration for call credentials created from AsyncAuthInterceptor + /// + public abstract void SetAsyncAuthInterceptorCredentials(object state, AsyncAuthInterceptor interceptor); + } +} diff --git a/src/csharp/Grpc.Core/Internal/DefaultCallCredentialsConfigurator.cs b/src/csharp/Grpc.Core/Internal/DefaultCallCredentialsConfigurator.cs new file mode 100644 index 00000000000..a2c53a173c5 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/DefaultCallCredentialsConfigurator.cs @@ -0,0 +1,85 @@ +#region Copyright notice and license + +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#endregion + +using System; +using System.Collections.Generic; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + /// + /// Creates native call credential objects from instances of CallCredentials. + /// + internal class DefaultCallCredentialsConfigurator : CallCredentialsConfiguratorBase + { + CallCredentialsSafeHandle nativeCredentials; + + public CallCredentialsSafeHandle NativeCredentials => nativeCredentials; + + public override void SetAsyncAuthInterceptorCredentials(object state, AsyncAuthInterceptor interceptor) + { + GrpcPreconditions.CheckState(nativeCredentials == null); + + var plugin = new NativeMetadataCredentialsPlugin(interceptor); + nativeCredentials = plugin.Credentials; + } + + public override void SetCompositeCredentials(object state, IReadOnlyList credentials) + { + GrpcPreconditions.CheckState(nativeCredentials == null); + + GrpcPreconditions.CheckArgument(credentials.Count >= 2); + nativeCredentials = CompositeToNativeRecursive(credentials, 0); + } + + // Recursive descent makes managing lifetime of intermediate CredentialSafeHandle instances easier. + // In practice, we won't usually see composites from more than two credentials anyway. + private CallCredentialsSafeHandle CompositeToNativeRecursive(IReadOnlyList credentials, int startIndex) + { + if (startIndex == credentials.Count - 1) + { + return credentials[startIndex].ToNativeCredentials(); + } + + using (var cred1 = credentials[startIndex].ToNativeCredentials()) + using (var cred2 = CompositeToNativeRecursive(credentials, startIndex + 1)) + { + var nativeComposite = CallCredentialsSafeHandle.CreateComposite(cred1, cred2); + if (nativeComposite.IsInvalid) + { + throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials."); + } + return nativeComposite; + } + } + } + + internal static class CallCredentialsExtensions + { + /// + /// Creates native object for the credentials. + /// + /// The native credentials. + public static CallCredentialsSafeHandle ToNativeCredentials(this CallCredentials credentials) + { + var configurator = new DefaultCallCredentialsConfigurator(); + credentials.InternalPopulateConfiguration(configurator, credentials); + return configurator.NativeCredentials; + } + } +}