Microsoft.Bot.Builder.Azure/Utils.cs
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license.
4 //
5 // Microsoft Bot Framework: http://botframework.com
6 //
7 // Bot Builder SDK Github:
8 // https://github.com/Microsoft/BotBuilder
9 //
10 // Copyright (c) Microsoft Corporation
11 // All rights reserved.
12 //
13 // MIT License:
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 
34 using System;
35 using System.Linq;
36 using System.Reflection;
37 using System.Runtime.Serialization;
38 using System.Text.RegularExpressions;
39 
41 using Microsoft.Bot.Connector;
42 
43 namespace Microsoft.Bot.Builder.Azure
44 {
48  public class AppSettingKeys
49  {
53  public const string StateEndpoint = "BotStateEndpoint";
54 
58  public const string OpenIdMetadata = "BotOpenIdMetadata";
59 
63  public const string AppId = "MicrosoftAppId";
64 
68  public const string Password = "MicrosoftAppPassword";
69 
73  public const string TableStorageConnectionString = "AzureWebJobsStorage";
74 
75 
79  public const string UseTableStorageForConversationState = "UseTableStorageForConversationState";
80  }
81 
82 
86  public sealed class Utils
87  {
93  public static string GetAppSetting(string key)
94  {
95  return Environment.GetEnvironmentVariable(key, EnvironmentVariableTarget.Process);
96  }
97 
103  public static string GetOpenIdConfigurationUrl(string key = AppSettingKeys.OpenIdMetadata)
104  {
105  var result = Utils.GetAppSetting(key);
106  if (String.IsNullOrEmpty(result))
107  {
109  }
110  return result;
111  }
112 
118  public static string GetStateApiUrl(string key = AppSettingKeys.StateEndpoint)
119  {
120  var result = Utils.GetAppSetting(key);
121  if (String.IsNullOrEmpty(result))
122  {
123  result = "https://state.botframework.com/";
124  }
125  return result;
126  }
127 
128  private static readonly Regex AzureFunctionAssembly = new Regex(@"(\S+)#\d+-\d+", RegexOptions.Compiled);
129 
130  internal static string RemoveAzureFunctionsDynamicSuffix(string name)
131  {
132  var match = AzureFunctionAssembly.Match(name);
133  if (match.Success)
134  {
135  return match.Groups[1].Value;
136  }
137 
138  return name;
139  }
140  }
141 
146  public class BotServiceDelegateSurrogate : Serialization.ISurrogateProvider
147  {
148 
152  public const string NameType = "type";
153 
157  public const string NameTarget = "target";
158 
162  public const string NameMethod = "method";
163 
167  public static readonly BindingFlags BindingFlags =
168  BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
169 
170  private readonly Assembly assembly;
171 
176  public BotServiceDelegateSurrogate(Assembly assembly)
177  {
178  SetField.NotNull(out this.assembly, nameof(assembly), assembly);
179  }
180 
181  bool Serialization.ISurrogateProvider.Handles(Type type, StreamingContext context, out int priority)
182  {
183  bool handles = typeof(Delegate).IsAssignableFrom(type);
184  priority = handles ? int.MaxValue : 0;
185  return handles;
186  }
187 
188 
189  void ISerializationSurrogate.GetObjectData(object obj, SerializationInfo info, StreamingContext context)
190  {
191  var lambda = (Delegate)obj;
192  info.AddValue(NameType, lambda.GetType());
193  info.AddValue(NameTarget, lambda.Target);
194  info.AddValue(NameMethod, lambda.Method);
195  }
196 
197  object ISerializationSurrogate.SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
198  {
199  var type = info.GetValue<Type>(NameType);
200  var target = info.GetValue<object>(NameTarget);
201  var method = info.GetValue<MethodInfo>(NameMethod);
202 
203  //this checks if azure function assembly is changed and maps the delegate to
204  //the new dynamic assembly generated.
205  if (method.Module.Assembly.FullName != this.assembly.FullName &&
206  Utils.RemoveAzureFunctionsDynamicSuffix(method.Module.Assembly.FullName) == Utils.RemoveAzureFunctionsDynamicSuffix(this.assembly.FullName))
207  {
208  return Delegate.CreateDelegate(type, target, this.GetMethodInfoFromCurrentAssembly(method));
209  }
210 
211  return Delegate.CreateDelegate(type, target, method);
212  }
213 
219  protected virtual MethodInfo GetMethodInfoFromCurrentAssembly(MethodInfo method)
220  {
221  var targetType = this.assembly.GetType(method.ReflectedType.FullName, throwOnError: true);
222  var methodParamTypes = method.GetParameters().Select(t => t.ParameterType).ToArray();
223 
224  return targetType.GetMethod(method.Name, BindingFlags, null, CallingConventions.Any, methodParamTypes, null);
225  }
226  }
227 
231  public sealed class BotServiceSerializationBinder : SerializationBinder
232  {
233  private readonly Assembly assembly;
234 
239  public BotServiceSerializationBinder(Assembly assembly)
240  {
241  SetField.NotNull(out this.assembly, nameof(assembly), assembly);
242  }
243 
249  public override Type BindToType(string assemblyName, string typeName)
250  {
251  if (Utils.RemoveAzureFunctionsDynamicSuffix(assemblyName) ==
252  Utils.RemoveAzureFunctionsDynamicSuffix(this.assembly.FullName))
253  {
254  assemblyName = this.assembly.FullName;
255  }
256 
257  // Get the type based on the type name and assembly name
258  // https://msdn.microsoft.com/en-us/library/system.runtime.serialization.serializationbinder(v=vs.110).aspx
259  return Type.GetType($"{typeName}, {assemblyName}");
260  }
261  }
262 
266  public sealed class ResolveAssembly : IDisposable
267  {
268  private readonly Assembly assembly;
269 
274  public static ResolveAssembly Create(Assembly assembly)
275  {
276  return new ResolveAssembly(assembly);
277  }
278 
283  private ResolveAssembly(Assembly assembly)
284  {
285  SetField.NotNull(out this.assembly, nameof(assembly), assembly);
286  AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
287  }
288 
289  void IDisposable.Dispose()
290  {
291  AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
292  }
293 
294  private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs arguments)
295  {
296  if (arguments.Name == this.assembly.FullName)
297  {
298  return assembly;
299  }
300 
301  return null;
302  }
303  }
304 }
override Type BindToType(string assemblyName, string typeName)
SerializationBinder.BindToType
A utility class for bots running on Azure.
Root namespace for the Microsoft Bot Connector SDK.
Definition: ActionTypes.cs:7
Extend ISerializationSurrogate with a "tester" method used by SurrogateSelector.
const string ToBotFromChannelOpenIdMetadataUrl
TO BOT FROM CHANNEL: OpenID metadata document for tokens coming from MSA
Definition: JwtConfig.cs:15
virtual MethodInfo GetMethodInfoFromCurrentAssembly(MethodInfo method)
Maps the method info to the new assembly.
Configuration for JWT tokens
Definition: JwtConfig.cs:10
A helper class responsible for resolving the calling assembly
static string GetAppSetting(string key)
Get value corresponding to the key from application settings.
Namespace for internal machinery that is not useful for most developers.
static string GetStateApiUrl(string key=AppSettingKeys.StateEndpoint)
Get the state api endpoint.
This surrogate is responsible for serialization of delegates and map them to the matching delegates i...
bool Handles(Type type, StreamingContext context, out int priority)
Determine whether this surrogate provider handles this type.
static ResolveAssembly Create(Assembly assembly)
Creates an instance of ResovelCallingAssembly
SerializationBinder responsible for mapping the matching types to the types in the assembly passed to...
BotServiceSerializationBinder(Assembly assembly)
Constructs an instance of bot service SerializationBinder.
static string GetOpenIdConfigurationUrl(string key=AppSettingKeys.OpenIdMetadata)
Get the open Id configuration url from application settings.
Namespace for the internal fibers machinery that is not useful for most developers.
Definition: Awaitable.cs:36
Root namespace for the Microsoft Bot Builder SDK.
const string StateEndpoint
The bot state endpoint key.
BotServiceDelegateSurrogate(Assembly assembly)
Constructs an instance of surrogate provider.