1 #region Copyright notice and license
2 
3 // Copyright 2019 The 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 Grpc.Core.Utils;
22 
23 namespace Grpc.Core.Internal
24 {
25     /// <summary>
26     /// Creates native call credential objects from instances of <c>CallCredentials</c>.
27     /// </summary>
28     internal class DefaultCallCredentialsConfigurator : CallCredentialsConfiguratorBase
29     {
30         CallCredentialsSafeHandle nativeCredentials;
31 
32         public CallCredentialsSafeHandle NativeCredentials => nativeCredentials;
33 
SetAsyncAuthInterceptorCredentials(object state, AsyncAuthInterceptor interceptor)34         public override void SetAsyncAuthInterceptorCredentials(object state, AsyncAuthInterceptor interceptor)
35         {
36             GrpcPreconditions.CheckState(nativeCredentials == null);
37 
38             var plugin = new NativeMetadataCredentialsPlugin(interceptor);
39             nativeCredentials = plugin.Credentials;
40         }
41 
SetCompositeCredentials(object state, IReadOnlyList<CallCredentials> credentials)42         public override void SetCompositeCredentials(object state, IReadOnlyList<CallCredentials> credentials)
43         {
44             GrpcPreconditions.CheckState(nativeCredentials == null);
45 
46             GrpcPreconditions.CheckArgument(credentials.Count >= 2);
47             nativeCredentials = CompositeToNativeRecursive(credentials, 0);
48         }
49 
50         // Recursive descent makes managing lifetime of intermediate CredentialSafeHandle instances easier.
51         // In practice, we won't usually see composites from more than two credentials anyway.
CompositeToNativeRecursive(IReadOnlyList<CallCredentials> credentials, int startIndex)52         private CallCredentialsSafeHandle CompositeToNativeRecursive(IReadOnlyList<CallCredentials> credentials, int startIndex)
53         {
54             if (startIndex == credentials.Count - 1)
55             {
56                 return credentials[startIndex].ToNativeCredentials();
57             }
58 
59             using (var cred1 = credentials[startIndex].ToNativeCredentials())
60             using (var cred2 = CompositeToNativeRecursive(credentials, startIndex + 1))
61             {
62                 var nativeComposite = CallCredentialsSafeHandle.CreateComposite(cred1, cred2);
63                 if (nativeComposite.IsInvalid)
64                 {
65                     throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials.");
66                 }
67                 return nativeComposite;
68             }
69         }
70     }
71 
72     internal static class CallCredentialsExtensions
73     {
74         /// <summary>
75         /// Creates native object for the credentials.
76         /// </summary>
77         /// <returns>The native credentials.</returns>
ToNativeCredentials(this CallCredentials credentials)78         public static CallCredentialsSafeHandle ToNativeCredentials(this CallCredentials credentials)
79         {
80             var configurator = new DefaultCallCredentialsConfigurator();
81             credentials.InternalPopulateConfiguration(configurator, credentials);
82             return configurator.NativeCredentials;
83         }
84     }
85 }
86