在插件中使用Secure Configuration
我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面的微软最有价值专家(Microsoft MVP),欢迎关注我的微信公众号 MSFTDynamics365erLuoYong ,回复409或者20200510可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!
在插件中使用Secure Configuration 的官方文档请参考 Write a plug-in 的 Pass configuration data to your plug-in 章节,本博文参考了 Joe Griffin 的 What is Unsecure/Secure Configuration on a Dynamics CRM/365 for Enterprise Plugin? 。在Action中获取输入参数和设置输出参数的值则参考了 Setting Output Parameters in Plugins for Custom Actions 。
首先我需要修改前面博文 Dynamics 365通过Action调用Graph API获取用户经理信息 创建的Action,将其所有的步骤去掉,然后激活。如下:
我使用的插件代码如下,我这里面获取了secure configuration的值。
using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Query; using System; using System.Net.Http; using System.IO; using System.Runtime.Serialization.Json; using System.Text; using System.Xml.Linq; using System.Linq; namespace DemoPlugins { public class GetUserManagerActionPost : IPlugin { private readonly string _secureString; public GetUserManagerActionPost(string unsecureString, string secureString) { if (String.IsNullOrEmpty(secureString)) { throw new InvalidPluginExecutionException("Secure strings are required for this plugin to execute."); } _secureString = secureString; } public void Execute(IServiceProvider serviceProvider) { ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); tracingService.Trace($"Enter {this.GetType()} on {DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss:ms")}"); IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); IOrganizationService orgSvc = serviceFactory.CreateOrganizationService(context.UserId); try { string userEmail = string.Empty; if (context.InputParameters.Contains("UserEmail") && context.InputParameters["UserEmail"] is string) { userEmail = context.InputParameters["UserEmail"].ToString(); } if (string.IsNullOrEmpty(userEmail)) { if (context.InputParameters.Contains("User") && context.InputParameters["User"] is EntityReference) { var user = (EntityReference)context.InputParameters["User"]; userEmail = orgSvc.Retrieve("systemuser", user.Id, new ColumnSet("internalemailaddress")).GetAttributeValue<string>("internalemailaddress"); } } if (string.IsNullOrEmpty(userEmail)) { throw new InvalidPluginExecutionException("EmailAddr is empty!"); } tracingService.Trace($"UserEmail = {userEmail} on {DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss:ms")}"); XElement secureXml = XElement.Parse(_secureString); var clientId = (from item in secureXml.Descendants("ClientId") select item).FirstOrDefault().Value; var clientSecret = (from item in secureXml.Descendants("ClientSecret") select item).FirstOrDefault().Value; var tenantId = (from item in secureXml.Descendants("TenantId") select item).FirstOrDefault().Value; var scope = (from item in secureXml.Descendants("Scope") select item).FirstOrDefault().Value; string tokenUrl = $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token"; AuthResponse authRepsonse = null; using (HttpClient httpClient = new HttpClient()) { var authRequest = new HttpRequestMessage(HttpMethod.Post, tokenUrl); var requestBody = new StringContent($"client_id={clientId}&scope={scope}&client_secret={clientSecret}&grant_type=client_credentials"); requestBody.Headers.ContentType.MediaType = "application/x-www-form-urlencoded"; authRequest.Content = requestBody; var authResponseStr = httpClient.SendAsync(authRequest).Result.Content.ReadAsStringAsync().Result; tracingService.Trace($"authResponseStr = {authResponseStr} on {DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss:ms")}"); using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(authResponseStr))) { DataContractJsonSerializer deseralizer = new DataContractJsonSerializer(typeof(AuthResponse)); authRepsonse = (AuthResponse)deseralizer.ReadObject(ms); } if (authRepsonse != null && !string.IsNullOrEmpty(authRepsonse.access_token)) { var request = new HttpRequestMessage(HttpMethod.Get, $"https://graph.microsoft.com/v1.0/users/{userEmail}/manager?$select=displayName,mail"); //request.Headers.Add("otherheraderitem", "headeritemvalue"); //request.Content = new StringContent(inJson, Encoding.UTF8, "application/json"); request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authRepsonse.access_token); var getUserResponseStr = httpClient.SendAsync(request).Result.Content.ReadAsStringAsync().Result; tracingService.Trace($"getUserResponseStr = {getUserResponseStr} on {DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss:ms")}"); using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(getUserResponseStr))) { DataContractJsonSerializer deseralizer = new DataContractJsonSerializer(typeof(GetUserResponse)); var getUserResponse = (GetUserResponse)deseralizer.ReadObject(ms); context.OutputParameters["ManagerName"] = getUserResponse.displayName; context.OutputParameters["ManagerEmail"] = getUserResponse.mail; } } else { throw new InvalidPluginExecutionException($"Get access token error!" + authResponseStr); } } tracingService.Trace($"Leave {this.GetType()} on {DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss:ms")}"); } catch (Exception ex) { tracingService.Trace($"{this.GetType()} encountered unexpected exception: {ex.Message + ex.StackTrace} on {DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss:ms")}"); throw new InvalidPluginExecutionException(ex.Message + ex.StackTrace); } } } public class AuthResponse { public string access_token { get; set; } public int expires_in { get; set; } public int ext_expires_in { get; set; } public string token_type { get; set; } } public class GetUserResponse { public string displayName { get; set; } public string mail { get; set; } } }
创建或者更新插件插件程序集后,需要注册步骤。
注册的步骤如下,注意,一定要注册在PostOperation阶段。
我使用的Secure Configuration示例:
<GraphAuthInfo> <ClientId>xxx</ClientId> <TenantId>xxxx</TenantId> <ClientSecret><![CDATA[xxx]]></ClientSecret> <Scope><![CDATA[https%3A%2F%2Fgraph.microsoft.com%2F.default]]></Scope> </GraphAuthInfo>