1 #region Copyright notice and license 2 3 // Copyright 2015 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.Threading.Tasks; 22 23 using Grpc.Core.Internal; 24 using Grpc.Core.Utils; 25 26 namespace Grpc.Core 27 { 28 /// <summary> 29 /// Client-side call credentials. Provide authorization with per-call granularity. 30 /// </summary> 31 public abstract class CallCredentials 32 { 33 /// <summary> 34 /// Composes multiple multiple <c>CallCredentials</c> objects into 35 /// a single <c>CallCredentials</c> object. 36 /// </summary> 37 /// <param name="credentials">credentials to compose</param> 38 /// <returns>The new <c>CompositeCallCredentials</c></returns> Compose(params CallCredentials[] credentials)39 public static CallCredentials Compose(params CallCredentials[] credentials) 40 { 41 return new CompositeCallCredentials(credentials); 42 } 43 44 /// <summary> 45 /// Creates a new instance of <c>CallCredentials</c> class from an 46 /// interceptor that can attach metadata to outgoing calls. 47 /// </summary> 48 /// <param name="interceptor">authentication interceptor</param> FromInterceptor(AsyncAuthInterceptor interceptor)49 public static CallCredentials FromInterceptor(AsyncAuthInterceptor interceptor) 50 { 51 return new MetadataCredentials(interceptor); 52 } 53 54 /// <summary> 55 /// Creates native object for the credentials. 56 /// </summary> 57 /// <returns>The native credentials.</returns> ToNativeCredentials()58 internal abstract CallCredentialsSafeHandle ToNativeCredentials(); 59 } 60 61 /// <summary> 62 /// Client-side credentials that delegate metadata based auth to an interceptor. 63 /// The interceptor is automatically invoked for each remote call that uses <c>MetadataCredentials.</c> 64 /// </summary> 65 internal sealed class MetadataCredentials : CallCredentials 66 { 67 readonly AsyncAuthInterceptor interceptor; 68 69 /// <summary> 70 /// Initializes a new instance of <c>MetadataCredentials</c> class. 71 /// </summary> 72 /// <param name="interceptor">authentication interceptor</param> MetadataCredentials(AsyncAuthInterceptor interceptor)73 public MetadataCredentials(AsyncAuthInterceptor interceptor) 74 { 75 this.interceptor = GrpcPreconditions.CheckNotNull(interceptor); 76 } 77 ToNativeCredentials()78 internal override CallCredentialsSafeHandle ToNativeCredentials() 79 { 80 NativeMetadataCredentialsPlugin plugin = new NativeMetadataCredentialsPlugin(interceptor); 81 return plugin.Credentials; 82 } 83 } 84 85 /// <summary> 86 /// Credentials that allow composing multiple credentials objects into one <see cref="CallCredentials"/> object. 87 /// </summary> 88 internal sealed class CompositeCallCredentials : CallCredentials 89 { 90 readonly List<CallCredentials> credentials; 91 92 /// <summary> 93 /// Initializes a new instance of <c>CompositeCallCredentials</c> class. 94 /// The resulting credentials object will be composite of all the credentials specified as parameters. 95 /// </summary> 96 /// <param name="credentials">credentials to compose</param> CompositeCallCredentials(params CallCredentials[] credentials)97 public CompositeCallCredentials(params CallCredentials[] credentials) 98 { 99 GrpcPreconditions.CheckArgument(credentials.Length >= 2, "Composite credentials object can only be created from 2 or more credentials."); 100 this.credentials = new List<CallCredentials>(credentials); 101 } 102 ToNativeCredentials()103 internal override CallCredentialsSafeHandle ToNativeCredentials() 104 { 105 return ToNativeRecursive(0); 106 } 107 108 // Recursive descent makes managing lifetime of intermediate CredentialSafeHandle instances easier. 109 // In practice, we won't usually see composites from more than two credentials anyway. ToNativeRecursive(int startIndex)110 private CallCredentialsSafeHandle ToNativeRecursive(int startIndex) 111 { 112 if (startIndex == credentials.Count - 1) 113 { 114 return credentials[startIndex].ToNativeCredentials(); 115 } 116 117 using (var cred1 = credentials[startIndex].ToNativeCredentials()) 118 using (var cred2 = ToNativeRecursive(startIndex + 1)) 119 { 120 var nativeComposite = CallCredentialsSafeHandle.CreateComposite(cred1, cred2); 121 if (nativeComposite.IsInvalid) 122 { 123 throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials."); 124 } 125 return nativeComposite; 126 } 127 } 128 } 129 } 130