diff --git a/src/Build.Common.StandardAndLegacy.props b/src/Build.Common.StandardAndLegacy.props index 572a886..663c988 100644 --- a/src/Build.Common.StandardAndLegacy.props +++ b/src/Build.Common.StandardAndLegacy.props @@ -10,6 +10,7 @@ + net462;net472;net48; IOperation diff --git a/src/Build.Shared.props b/src/Build.Shared.props index bea7d4d..d7cbcab 100644 --- a/src/Build.Shared.props +++ b/src/Build.Shared.props @@ -4,17 +4,34 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 2.9.1 3.19.8 - 4.25.0 + 4.35.1 4.6.4784-weekly-2107.4 4.6.4784-weekly-2107.4 11.0.2 2.3.20 - 9.0.2.25 + 9.0.2.42 + 9.0.2.34 2.0.11 + 0.4.20 + 3.1.0 + 3.1.8 + 4.16.0 2.4.1 + 2.4.3 5.10.3 + + + false + false + false + false + false + false + false + false + false diff --git a/src/GeneralTools/DataverseClient/Client/Auth/AuthProcessor.cs b/src/GeneralTools/DataverseClient/Client/Auth/AuthProcessor.cs index 0640895..50e1bbe 100644 --- a/src/GeneralTools/DataverseClient/Client/Auth/AuthProcessor.cs +++ b/src/GeneralTools/DataverseClient/Client/Auth/AuthProcessor.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Identity.Client; +using Microsoft.PowerPlatform.Dataverse.Client.Auth.TokenCache; using Microsoft.PowerPlatform.Dataverse.Client.Utils; using Microsoft.Xrm.Sdk.WebServiceClient; using System; @@ -16,513 +17,575 @@ namespace Microsoft.PowerPlatform.Dataverse.Client.Auth { internal class AuthProcessor { - /// - /// Executes Authentication against a service - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Certificate of provided to login with - /// (optional) Initialized CdsTraceLogger Object - /// (optional) if set, tries to login as the current user. - /// Object of either confidential or public client - /// - /// indicates if the serviceURI should be updated to include the /web?sdk version - /// AuthenticationResult containing a JWT Token for the requested Resource and user/app - internal async static Task ExecuteAuthenticateServiceProcessAsync( - Uri serviceUrl, - ClientCredentials clientCredentials, - X509Certificate2 userCert, - string clientId, - Uri redirectUri, - PromptBehavior promptBehavior, - bool isOnPrem, - string authority, - object msalAuthClient , - DataverseTraceLogger logSink = null, - bool useDefaultCreds = false, - SecureString clientSecret = null, - bool addVersionInfoToUri = true, - IAccount user = null - ) - { - ExecuteAuthenticationResults processResult = new ExecuteAuthenticationResults(); - bool createdLogSource = false; - - AuthenticationResult authenticationResult = null; - - try - { - if (logSink == null) - { - // when set, the log source is locally created. - createdLogSource = true; - logSink = new DataverseTraceLogger(); - } - - string Authority = string.Empty; - string Resource = string.Empty; - - bool clientCredentialsCheck = clientCredentials != null && clientCredentials.UserName != null && !string.IsNullOrEmpty(clientCredentials.UserName.UserName) && !string.IsNullOrEmpty(clientCredentials.UserName.Password); - Resource = serviceUrl.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped); - if (!Resource.EndsWith("/")) - Resource += "/"; - - if (addVersionInfoToUri) - processResult.TargetServiceUrl = GetUriBuilderWithVersion(serviceUrl).Uri; - else - processResult.TargetServiceUrl = serviceUrl; - - if (!string.IsNullOrWhiteSpace(authority)) - { - //Overriding the tenant specific authority if clientCredentials are null - Authority = authority; - } - else - { - var rslt = GetAuthorityFromTargetServiceAsync(ClientServiceProviders.Instance.GetService(), processResult.TargetServiceUrl, logSink).ConfigureAwait(false).GetAwaiter().GetResult(); - if (!string.IsNullOrEmpty(rslt.Authority)) + /// + /// Executes Authentication against a service + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Certificate of provided to login with + /// (optional) Initialized CdsTraceLogger Object + /// (optional) if set, tries to login as the current user. + /// Object of either confidential or public client + /// + /// indicates if the serviceURI should be updated to include the /web?sdk version + /// Memory Token Cache + /// path to the token cache. + /// AuthenticationResult containing a JWT Token for the requested Resource and user/app + internal async static Task ExecuteAuthenticateServiceProcessAsync( + Uri serviceUrl, + ClientCredentials clientCredentials, + X509Certificate2 userCert, + string clientId, + Uri redirectUri, + PromptBehavior promptBehavior, + bool isOnPrem, + string authority, + object msalAuthClient, + DataverseTraceLogger logSink = null, + bool useDefaultCreds = false, + SecureString clientSecret = null, + bool addVersionInfoToUri = true, + IAccount user = null, + MemoryBackedTokenCache memoryBackedTokenCache = null, + string tokenCacheStorePath = null + ) + { + ExecuteAuthenticationResults processResult = new ExecuteAuthenticationResults(); + bool createdLogSource = false; + + AuthenticationResult authenticationResult = null; + + try + { + if (logSink == null) + { + // when set, the log source is locally created. + createdLogSource = true; + logSink = new DataverseTraceLogger(); + } + + string Authority = string.Empty; + string Resource = string.Empty; + + bool clientCredentialsCheck = clientCredentials != null && clientCredentials.UserName != null && !string.IsNullOrEmpty(clientCredentials.UserName.UserName) && !string.IsNullOrEmpty(clientCredentials.UserName.Password); + Resource = serviceUrl.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped); + if (!Resource.EndsWith("/")) + Resource += "/"; + + if (addVersionInfoToUri) + processResult.TargetServiceUrl = GetUriBuilderWithVersion(serviceUrl).Uri; + else + processResult.TargetServiceUrl = serviceUrl; + + if (!string.IsNullOrWhiteSpace(authority)) + { + //Overriding the tenant specific authority if clientCredentials are null + Authority = authority; + } + else + { + var rslt = GetAuthorityFromTargetServiceAsync(ClientServiceProviders.Instance.GetService(), processResult.TargetServiceUrl, logSink).ConfigureAwait(false).GetAwaiter().GetResult(); + if (!string.IsNullOrEmpty(rslt.Authority)) { - Authority = rslt.Authority; - Resource = rslt.Resource; + Authority = rslt.Authority; + Resource = rslt.Resource; } - else - throw new ArgumentNullException("Authority", "Need a non-empty authority"); - } - // clientCredentialsCheck = false; // Forcing system to provide a UX popup vs UID/PW + else + throw new ArgumentNullException("Authority", "Need a non-empty authority"); + } + // clientCredentialsCheck = false; // Forcing system to provide a UX popup vs UID/PW - // Assign outbound properties. - processResult.Resource = Resource; - processResult.Authority = Authority; + // Assign outbound properties. + processResult.Resource = Resource; + processResult.Authority = Authority; - logSink.Log("AuthenticateService - found authority with name " + (string.IsNullOrEmpty(Authority) ? "" : Authority)); - logSink.Log("AuthenticateService - found resource with name " + (string.IsNullOrEmpty(Resource) ? "" : Resource)); + logSink.Log("AuthenticateService - found authority with name " + (string.IsNullOrEmpty(Authority) ? "" : Authority)); + logSink.Log("AuthenticateService - found resource with name " + (string.IsNullOrEmpty(Resource) ? "" : Resource)); - Uri ResourceUri = new Uri(Resource); + Uri ResourceUri = new Uri(Resource); // Add Scope, List Scopes = Utilities.AddScope($"{Resource}/user_impersonation"); - AuthenticationResult _authenticationResult = null; - if (userCert != null || clientSecret != null) - { - // Add Scope, - Scopes.Clear(); - Scopes = Utilities.AddScope($"{Resource}.default" , Scopes); - - IConfidentialClientApplication cApp = null; - ConfidentialClientApplicationBuilder cAppBuilder = null; - - if (msalAuthClient is IConfidentialClientApplication) - { - cApp = (IConfidentialClientApplication)msalAuthClient; - } - else - { - cAppBuilder = ConfidentialClientApplicationBuilder.CreateWithApplicationOptions( - new ConfidentialClientApplicationOptions() - { - ClientId = clientId, - EnablePiiLogging = true, - LogLevel = LogLevel.Verbose, - }) - .WithAuthority(Authority) - .WithLogging(Microsoft.PowerPlatform.Dataverse.Client.Utils.ADALLoggerCallBack.Log); - } - - if (userCert != null) - { - logSink.Log("Initial ObtainAccessToken - CERT", TraceEventType.Verbose); - cApp = cAppBuilder.WithCertificate(userCert).Build(); - _authenticationResult = await ObtainAccessTokenAsync(cApp, Scopes, logSink).ConfigureAwait(false); - } - else - { - if (clientSecret != null) - { - logSink.Log("Initial ObtainAccessToken - Client Secret", TraceEventType.Verbose); - cApp = cAppBuilder.WithClientSecret(clientSecret.ToUnsecureString()).Build(); - _authenticationResult = await ObtainAccessTokenAsync(cApp, Scopes, logSink).ConfigureAwait(false); - } - else - throw new Exception("Invalid Cert or Client Secret Auth flow"); - } - - // Update the MSAL Client handed back. - processResult.MsalAuthClient = cApp; - } - else - { - PublicClientApplicationBuilder cApp = null; - IPublicClientApplication pApp = null; - if (msalAuthClient is IPublicClientApplication) - { - pApp = (IPublicClientApplication)msalAuthClient; - } - else - { - cApp = PublicClientApplicationBuilder.CreateWithApplicationOptions( - new PublicClientApplicationOptions() - { - ClientId = clientId, - EnablePiiLogging = true, - RedirectUri = redirectUri.ToString(), - LogLevel = LogLevel.Verbose, - }) - .WithAuthority(Authority) - .WithLogging(Microsoft.PowerPlatform.Dataverse.Client.Utils.ADALLoggerCallBack.Log); - - pApp = cApp.Build(); - } - - //Run user Auth flow. - _authenticationResult = await ObtainAccessTokenAsync(pApp, Scopes, user, promptBehavior, clientCredentials, useDefaultCreds, logSink).ConfigureAwait(false); - - // Assign the application back out - processResult.MsalAuthClient = pApp; - - //Assigning the authority to ref object to pass back to ConnMgr to store the latest Authority in Credential Manager. - authority = Authority; - } - - if (_authenticationResult != null && _authenticationResult.Account != null) - { - //To use same userId while connecting to OrgService (ConnectAndInitCrmOrgService) - //_userId = _authenticationResult.Account; - processResult.UserIdent = _authenticationResult.Account; - } - - if (null == _authenticationResult) - { - throw new ArgumentNullException("AuthenticationResult"); - } - authenticationResult = _authenticationResult; - processResult.MsalAuthResult = authenticationResult; - } - catch (AggregateException ex) - { - if (ex.InnerException is Microsoft.Identity.Client.MsalException) - { - var errorHandledResult = await ProcessAdalExecptionAsync(serviceUrl, clientCredentials, userCert, clientId, redirectUri, promptBehavior, isOnPrem, authority , msalAuthClient, logSink, useDefaultCreds , (Microsoft.Identity.Client.MsalException)ex.InnerException).ConfigureAwait(false); - if (errorHandledResult != null) - processResult = errorHandledResult; - } - else - { - logSink.Log("ERROR REQUESTING Token FROM THE Authentication context - General ADAL Error", TraceEventType.Error, ex); - logSink.Log(ex); - throw; - } - } - catch (Microsoft.Identity.Client.MsalException ex) - { - var errorHandledResult = await ProcessAdalExecptionAsync(serviceUrl, clientCredentials, userCert, clientId, redirectUri, promptBehavior, isOnPrem, authority, msalAuthClient, logSink, useDefaultCreds, ex); - if (errorHandledResult != null) - processResult = errorHandledResult; - } - catch (System.Exception ex) - { - logSink.Log("ERROR REQUESTING Token FROM THE Authentication context", TraceEventType.Error); - logSink.Log(ex); - throw; - } - finally - { - if (createdLogSource) // Only dispose it if it was created locally. - logSink.Dispose(); - } - return processResult; - } - - - - /// - /// Token refresh flow for MSAL User Flows. - /// - /// MSAL Client to use. - /// Scopes to send in. - /// - /// prompting behavior - /// user credential package - /// should system default creds be used - /// logger to write logs too. - /// - internal async static Task ObtainAccessTokenAsync( - IPublicClientApplication publicAppClient, - List scopes, - IAccount account, - PromptBehavior promptBehavior, - ClientCredentials clientCredentials, - bool useDefaultCreds = false, - DataverseTraceLogger logSink = null) - { - // This works for user Auth flows. - AuthenticationResult _authenticationResult = null; - bool clientCredentialsCheck = clientCredentials != null && clientCredentials.UserName != null && !string.IsNullOrEmpty(clientCredentials.UserName.UserName) && !string.IsNullOrEmpty(clientCredentials.UserName.Password); - // Login user hint - string loginUserHint = (clientCredentials != null && clientCredentials.UserName != null) ? clientCredentials.UserName.UserName : string.Empty; - if (publicAppClient != null) - { - if (clientCredentialsCheck && !useDefaultCreds && !(promptBehavior == PromptBehavior.Always || promptBehavior == PromptBehavior.SelectAccount)) - { - if (account != null) - { - _authenticationResult = await publicAppClient.AcquireTokenSilent(scopes, account).ExecuteAsync().ConfigureAwait(false); - } - else + AuthenticationResult _authenticationResult = null; + if (userCert != null || clientSecret != null) + { + // Add Scope, + Scopes.Clear(); + Scopes = Utilities.AddScope($"{Resource}.default", Scopes); + + IConfidentialClientApplication cApp = null; + ConfidentialClientApplicationBuilder cAppBuilder = null; + + if (msalAuthClient is IConfidentialClientApplication) + { + cApp = (IConfidentialClientApplication)msalAuthClient; + } + else { - _authenticationResult = await publicAppClient.AcquireTokenByUsernamePassword(scopes, clientCredentials.UserName.UserName, ServiceClient.MakeSecureString(clientCredentials.UserName.Password)).ExecuteAsync().ConfigureAwait(false); + cAppBuilder = ConfidentialClientApplicationBuilder.CreateWithApplicationOptions( + new ConfidentialClientApplicationOptions() + { + ClientId = clientId, + EnablePiiLogging = true, + LogLevel = LogLevel.Verbose, + }) + .WithAuthority(Authority) + .WithLegacyCacheCompatibility(false) + .WithHttpClientFactory(new MSALHttpClientFactory()) + .WithLogging(MSALLoggerCallBack.Log); } - } - else - { - if (useDefaultCreds) - { - if (!string.IsNullOrEmpty(loginUserHint)) - { - _authenticationResult = await publicAppClient.AcquireTokenByIntegratedWindowsAuth(scopes).WithUsername(loginUserHint).ExecuteAsync().ConfigureAwait(false); - } - else - { - _authenticationResult = await publicAppClient.AcquireTokenByIntegratedWindowsAuth(scopes).ExecuteAsync().ConfigureAwait(false); - } - } - else - { - logSink.Log(string.Format("ObtainAccessToken - PROMPT - Behavior: {0}", promptBehavior), TraceEventType.Verbose); - Microsoft.Identity.Client.Prompt? userPrompt = null; - switch (promptBehavior) - { - case PromptBehavior.Auto: - break; - case PromptBehavior.Always: - userPrompt = Microsoft.Identity.Client.Prompt.ForceLogin; - break; - case PromptBehavior.Never: - case PromptBehavior.RefreshSession: - userPrompt = Microsoft.Identity.Client.Prompt.NoPrompt; - break; - case PromptBehavior.SelectAccount: - userPrompt = Microsoft.Identity.Client.Prompt.SelectAccount; - break; - default: - break; - } - - if (userPrompt != null) - { - _authenticationResult = await publicAppClient.AcquireTokenInteractive(scopes).WithLoginHint(loginUserHint).WithPrompt(userPrompt.Value).ExecuteAsync().ConfigureAwait(false); - } - else - { - if (account != null) - { - _authenticationResult = await publicAppClient.AcquireTokenSilent(scopes, account).ExecuteAsync().ConfigureAwait(false); - } - else - { - _authenticationResult = await publicAppClient.AcquireTokenInteractive(scopes).WithLoginHint(loginUserHint).ExecuteAsync().ConfigureAwait(false); - } - } - } - } - } - else - { - // throw here. - } - return _authenticationResult; - } - - - /// - /// Acquires Confidential client token. - /// - /// Confidential client application - /// Scope List - /// Logger to use - /// Authentication Result with updated token - internal async static Task ObtainAccessTokenAsync( - IConfidentialClientApplication confidentialAppClient, - List scopes, - DataverseTraceLogger logSink = null) - { - // This works for user Auth flows. - AuthenticationResult _authenticationResult = null; - if (confidentialAppClient != null) - { - _authenticationResult = await confidentialAppClient.AcquireTokenForClient(scopes).ExecuteAsync().ConfigureAwait(false); - } - else - { - // throw here. - } - return _authenticationResult; - } - - - /// - /// Forming version tagged UriBuilder - /// - /// - /// - internal static UriBuilder GetUriBuilderWithVersion(Uri discoveryServiceUri) - { - UriBuilder webUrlBuilder = new UriBuilder(discoveryServiceUri); - string webPath = "web"; - - if (!discoveryServiceUri.AbsolutePath.EndsWith(webPath)) - { - if (discoveryServiceUri.AbsolutePath.EndsWith("/")) - webUrlBuilder.Path = string.Concat(webUrlBuilder.Path, webPath); - else - webUrlBuilder.Path = string.Concat(webUrlBuilder.Path, "/", webPath); - } - - UriBuilder versionTaggedUriBuilder = new UriBuilder(webUrlBuilder.Uri); - string version = FileVersionInfo.GetVersionInfo(typeof(OrganizationWebProxyClient).Assembly.Location).FileVersion; - string versionQueryStringParameter = string.Format("SDKClientVersion={0}", version); - - if (string.IsNullOrEmpty(versionTaggedUriBuilder.Query)) - { - versionTaggedUriBuilder.Query = versionQueryStringParameter; - } - else if (!versionTaggedUriBuilder.Query.Contains("SDKClientVersion=")) - { - versionTaggedUriBuilder.Query = string.Format("{0}&{1}", versionTaggedUriBuilder.Query, versionQueryStringParameter); - } - - return versionTaggedUriBuilder; - } - - - private const string AuthenticateHeader = "WWW-Authenticate"; - private const string Bearer = "bearer"; - private const string AuthorityKey = "authorization_uri"; - private const string ResourceKey = "resource_id"; - - internal class AuthRoutingProperties + + // initialization of memory cache if its not already initialized. + if (memoryBackedTokenCache == null) + memoryBackedTokenCache = new MemoryBackedTokenCache(new MemoryTokenCacheOptions()); + + if (userCert != null) + { + logSink.Log("Initial ObtainAccessToken - CERT", TraceEventType.Verbose); + cApp = cAppBuilder.WithCertificate(userCert).Build(); + memoryBackedTokenCache.Initialize(cApp.AppTokenCache); + _authenticationResult = await ObtainAccessTokenAsync(cApp, Scopes, logSink).ConfigureAwait(false); + } + else + { + if (clientSecret != null) + { + logSink.Log("Initial ObtainAccessToken - Client Secret", TraceEventType.Verbose); + cApp = cAppBuilder.WithClientSecret(clientSecret.ToUnsecureString()).WithCacheSynchronization(false).Build(); + memoryBackedTokenCache.Initialize(cApp.AppTokenCache); + _authenticationResult = await ObtainAccessTokenAsync(cApp, Scopes, logSink).ConfigureAwait(false); + } + else + throw new Exception("Invalid Cert or Client Secret Auth flow"); + } + // Update the MSAL Client handed back. + processResult.MsalAuthClient = cApp; + processResult.MemTokenCache = memoryBackedTokenCache; + } + else + { + PublicClientApplicationBuilder cApp = null; + IPublicClientApplication pApp = null; + if (msalAuthClient is IPublicClientApplication) + { + pApp = (IPublicClientApplication)msalAuthClient; + } + else + { + cApp = PublicClientApplicationBuilder.CreateWithApplicationOptions( + new PublicClientApplicationOptions() + { + ClientId = clientId, + EnablePiiLogging = true, + RedirectUri = redirectUri.ToString(), + LogLevel = LogLevel.Verbose, + }) + .WithAuthority(Authority) + .WithLegacyCacheCompatibility(false) + .WithLogging(MSALLoggerCallBack.Log); + + pApp = cApp.Build(); + + // if Null, do not use it, if not null but empty initialize with default paths, else use provided path. + if (tokenCacheStorePath != null) + { + var f = new FileBackedTokenCache(new FileBackedTokenCacheHints(tokenCacheStorePath)); + await f.Initialize(pApp.UserTokenCache); + } + } + + //Run user Auth flow. + _authenticationResult = await ObtainAccessTokenAsync(pApp, Scopes, user, promptBehavior, clientCredentials, useDefaultCreds, logSink).ConfigureAwait(false); + + // Assign the application back out + processResult.MsalAuthClient = pApp; + + //Assigning the authority to ref object to pass back to ConnMgr to store the latest Authority in Credential Manager. + authority = Authority; + } + + if (_authenticationResult != null && _authenticationResult.Account != null) + { + //To use same userId while connecting to OrgService + //_userId = _authenticationResult.Account; + processResult.UserIdent = _authenticationResult.Account; + } + + if (null == _authenticationResult) + { + throw new ArgumentNullException("AuthenticationResult"); + } + authenticationResult = _authenticationResult; + processResult.MsalAuthResult = authenticationResult; + } + catch (AggregateException ex) + { + if (ex.InnerException is MsalException) + { + var errorHandledResult = await ProcessMsalExecptionAsync(serviceUrl, clientCredentials, userCert, clientId, redirectUri, promptBehavior, isOnPrem, authority, msalAuthClient, logSink, useDefaultCreds, msalEx: (MsalException)ex.InnerException, memoryBackedTokenCache: memoryBackedTokenCache, tokenCacheStorePath: tokenCacheStorePath).ConfigureAwait(false); + if (errorHandledResult != null) + processResult = errorHandledResult; + } + else + { + logSink.Log("ERROR REQUESTING Token FROM THE Authentication context - General MSAL Error", TraceEventType.Error, ex); + logSink.Log(ex); + throw; + } + } + catch (MsalException ex) + { + var errorHandledResult = await ProcessMsalExecptionAsync(serviceUrl, clientCredentials, userCert, clientId, redirectUri, promptBehavior, isOnPrem, authority, msalAuthClient, logSink, useDefaultCreds, msalEx: ex, memoryBackedTokenCache: memoryBackedTokenCache, tokenCacheStorePath: tokenCacheStorePath); + if (errorHandledResult != null) + processResult = errorHandledResult; + } + catch (Exception ex) + { + logSink.Log("ERROR REQUESTING Token FROM THE Authentication context", TraceEventType.Error); + logSink.Log(ex); + throw; + } + finally + { + if (createdLogSource) // Only dispose it if it was created locally. + logSink.Dispose(); + } + return processResult; + } + + + + /// + /// Token refresh flow for MSAL User Flows. + /// + /// MSAL Client to use. + /// Scopes to send in. + /// + /// prompting behavior + /// user credential package + /// should system default creds be used + /// logger to write logs too. + /// + internal async static Task ObtainAccessTokenAsync( + IPublicClientApplication publicAppClient, + List scopes, + IAccount account, + PromptBehavior promptBehavior, + ClientCredentials clientCredentials, + bool useDefaultCreds = false, + DataverseTraceLogger logSink = null) + { + // This works for user Auth flows. + AuthenticationResult _authenticationResult = null; + bool clientCredentialsCheck = clientCredentials != null && clientCredentials.UserName != null && !string.IsNullOrEmpty(clientCredentials.UserName.UserName) && !string.IsNullOrEmpty(clientCredentials.UserName.Password); + // Login user hint + string loginUserHint = (clientCredentials != null && clientCredentials.UserName != null) ? clientCredentials.UserName.UserName : string.Empty; + if (publicAppClient != null) + { + if (account == null) + account = await TryGetAccountFromCache(publicAppClient, loginUserHint).ConfigureAwait(false); + + if (clientCredentialsCheck && !useDefaultCreds && !(promptBehavior == PromptBehavior.Always || promptBehavior == PromptBehavior.SelectAccount)) + { + if (account != null) + { + _authenticationResult = await publicAppClient.AcquireTokenSilent(scopes, account).ExecuteAsync().ConfigureAwait(false); + } + else + { + _authenticationResult = await publicAppClient.AcquireTokenByUsernamePassword(scopes, clientCredentials.UserName.UserName, ServiceClient.MakeSecureString(clientCredentials.UserName.Password)).ExecuteAsync().ConfigureAwait(false); + } + } + else + { + if (useDefaultCreds) + { + if (!string.IsNullOrEmpty(loginUserHint)) + { + _authenticationResult = await publicAppClient.AcquireTokenByIntegratedWindowsAuth(scopes).WithUsername(loginUserHint).ExecuteAsync().ConfigureAwait(false); + } + else + { + _authenticationResult = await publicAppClient.AcquireTokenByIntegratedWindowsAuth(scopes).ExecuteAsync().ConfigureAwait(false); + } + } + else + { + //logSink.Log(string.Format("ObtainAccessToken - PROMPT - Behavior: {0}", promptBehavior), TraceEventType.Verbose); + Microsoft.Identity.Client.Prompt? userPrompt = null; + switch (promptBehavior) + { + case PromptBehavior.Auto: + break; + case PromptBehavior.Always: + userPrompt = Microsoft.Identity.Client.Prompt.ForceLogin; + break; + case PromptBehavior.Never: + case PromptBehavior.RefreshSession: + userPrompt = Microsoft.Identity.Client.Prompt.NoPrompt; + break; + case PromptBehavior.SelectAccount: + userPrompt = Microsoft.Identity.Client.Prompt.SelectAccount; + break; + default: + break; + } + + if (userPrompt != null) + { + _authenticationResult = await publicAppClient.AcquireTokenInteractive(scopes).WithLoginHint(loginUserHint).WithPrompt(userPrompt.Value).ExecuteAsync().ConfigureAwait(false); + } + else + { + if (account != null) + { + _authenticationResult = await publicAppClient.AcquireTokenSilent(scopes, account).ExecuteAsync().ConfigureAwait(false); + } + else + { + _authenticationResult = await publicAppClient.AcquireTokenInteractive(scopes).WithLoginHint(loginUserHint).ExecuteAsync().ConfigureAwait(false); + } + } + } + } + } + else + { + // throw here. + } + return _authenticationResult; + } + + + /// + /// Acquires Confidential client token. + /// + /// Confidential client application + /// Scope List + /// Logger to use + /// Authentication Result with updated token + internal async static Task ObtainAccessTokenAsync( + IConfidentialClientApplication confidentialAppClient, + List scopes, + DataverseTraceLogger logSink = null) + { + // This works for user Auth flows. + AuthenticationResult _authenticationResult = null; + if (confidentialAppClient != null) + { + _authenticationResult = await confidentialAppClient.AcquireTokenForClient(scopes).ExecuteAsync().ConfigureAwait(false); + } + else + { + // throw here. + } + return _authenticationResult; + } + + + /// + /// For Public Client, check the local cache to see if there is already a entry there for this user. + /// + /// PubClient instance. + /// UID of the user being searched for + /// + internal async static Task TryGetAccountFromCache(IPublicClientApplication publicAppClient, string loginHint) + { + try + { + if (publicAppClient != null) + { + var accList = await publicAppClient.GetAccountsAsync().ConfigureAwait(false); + if (accList != null && accList.Count() > 0) + { + return accList.FirstOrDefault(w => w.Username.Equals(loginHint, StringComparison.OrdinalIgnoreCase)); + } + } + } + finally { } + return null; + } + + /// + /// Forming version tagged UriBuilder + /// + /// + /// + internal static UriBuilder GetUriBuilderWithVersion(Uri discoveryServiceUri) + { + UriBuilder webUrlBuilder = new UriBuilder(discoveryServiceUri); + string webPath = "web"; + + if (!discoveryServiceUri.AbsolutePath.EndsWith(webPath)) + { + if (discoveryServiceUri.AbsolutePath.EndsWith("/")) + webUrlBuilder.Path = string.Concat(webUrlBuilder.Path, webPath); + else + webUrlBuilder.Path = string.Concat(webUrlBuilder.Path, "/", webPath); + } + + UriBuilder versionTaggedUriBuilder = new UriBuilder(webUrlBuilder.Uri); + string version = FileVersionInfo.GetVersionInfo(typeof(Xrm.Sdk.Organization.OrganizationDetail).Assembly.Location).FileVersion; + string versionQueryStringParameter = string.Format("SDKClientVersion={0}", version); + + if (string.IsNullOrEmpty(versionTaggedUriBuilder.Query)) + { + versionTaggedUriBuilder.Query = versionQueryStringParameter; + } + else if (!versionTaggedUriBuilder.Query.Contains("SDKClientVersion=")) + { + versionTaggedUriBuilder.Query = string.Format("{0}&{1}", versionTaggedUriBuilder.Query, versionQueryStringParameter); + } + + return versionTaggedUriBuilder; + } + + + private const string AuthenticateHeader = "WWW-Authenticate"; + private const string Bearer = "bearer"; + private const string AuthorityKey = "authorization_uri"; + private const string ResourceKey = "resource_id"; + + internal class AuthRoutingProperties { public string Authority { get; set; } public string Resource { get; set; } } - /// - /// Get authority and resource for this instance. - /// - /// URI to query - /// Logger to write info too - /// HTTP Client factory to use for this request. - /// - private static async Task GetAuthorityFromTargetServiceAsync(IHttpClientFactory clientFactory, Uri targetServiceUrl , DataverseTraceLogger logger ) + /// + /// Get authority and resource for this instance. + /// + /// URI to query + /// Logger to write info too + /// HTTP Client factory to use for this request. + /// + private static async Task GetAuthorityFromTargetServiceAsync(IHttpClientFactory clientFactory, Uri targetServiceUrl, DataverseTraceLogger logger) { - AuthRoutingProperties authRoutingProperties = new AuthRoutingProperties(); - var client = clientFactory.CreateClient("DataverseHttpClientFactory"); - var rslt = await client.GetAsync(targetServiceUrl).ConfigureAwait(false); - - if ( rslt.StatusCode == System.Net.HttpStatusCode.NotFound || rslt.StatusCode == System.Net.HttpStatusCode.BadRequest ) - { - // didnt find endpoint. - logger.Log($"Failed to get Authority and Resource error. Attempt to Access Endpoint {targetServiceUrl.ToString()} resulted in {rslt.StatusCode}.", TraceEventType.Error); - return authRoutingProperties; - } - - if (rslt.Headers.Contains("WWW-Authenticate")) + AuthRoutingProperties authRoutingProperties = new AuthRoutingProperties(); + var client = clientFactory.CreateClient("DataverseHttpClientFactory"); + var rslt = await client.GetAsync(targetServiceUrl).ConfigureAwait(false); + + if (rslt.StatusCode == System.Net.HttpStatusCode.NotFound || rslt.StatusCode == System.Net.HttpStatusCode.BadRequest) + { + // didn't find endpoint. + logger.Log($"Failed to get Authority and Resource error. Attempt to Access Endpoint {targetServiceUrl.ToString()} resulted in {rslt.StatusCode}.", TraceEventType.Error); + return authRoutingProperties; + } + + if (rslt.Headers.Contains("WWW-Authenticate")) { - var authenticateHeader = rslt.Headers.GetValues("WWW-Authenticate").FirstOrDefault(); - authenticateHeader = authenticateHeader.Trim(); - - // This also checks for cases like "BearerXXXX authorization_uri=...." and "Bearer" and "Bearer " - if (!authenticateHeader.StartsWith(Bearer, StringComparison.OrdinalIgnoreCase) - || authenticateHeader.Length < Bearer.Length + 2 - || !char.IsWhiteSpace(authenticateHeader[Bearer.Length])) - { - //var ex = new ArgumentException(AdalErrorMessage.InvalidAuthenticateHeaderFormat, - // nameof(authenticateHeader)); - //CoreLoggerBase.Default.Error(AdalErrorMessage.InvalidAuthenticateHeaderFormat); - //CoreLoggerBase.Default.ErrorPii(ex); - //throw ex; - } - - authenticateHeader = authenticateHeader.Substring(Bearer.Length).Trim(); - - IDictionary authenticateHeaderItems = null; - try - { - authenticateHeaderItems = - EncodingHelper.ParseKeyValueListStrict(authenticateHeader, ',', false, true); - } - catch //(ArgumentException ex) - { - //var newEx = new ArgumentException(AdalErrorMessage.InvalidAuthenticateHeaderFormat, - // nameof(authenticateHeader), ex); - //CoreLoggerBase.Default.Error(AdalErrorMessage.InvalidAuthenticateHeaderFormat); - //CoreLoggerBase.Default.ErrorPii(newEx); - //throw newEx; - } - - if (authenticateHeaderItems != null) - { - string param; - authenticateHeaderItems.TryGetValue(AuthorityKey, out param); - authRoutingProperties.Authority = - param.Replace("oauth2/authorize", "") // swap out the old oAuth pattern. - .Replace("common" , "organizations"); // swap common for organizations because MSAL reasons. - authenticateHeaderItems.TryGetValue(ResourceKey, out param); - authRoutingProperties.Resource = param; - } - } - - return authRoutingProperties; - } - - /// - /// Process ADAL exception and provide common handlers. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - private async static Task ProcessAdalExecptionAsync(Uri serviceUrl, ClientCredentials clientCredentials, X509Certificate2 userCert, string clientId, Uri redirectUri, PromptBehavior promptBehavior, bool isOnPrem, string authority , object msalAuthClient, DataverseTraceLogger logSink, bool useDefaultCreds, Microsoft.Identity.Client.MsalException adalEx) - { - if (adalEx.ErrorCode.Equals("interaction_required", StringComparison.OrdinalIgnoreCase) || - adalEx.ErrorCode.Equals("user_password_expired", StringComparison.OrdinalIgnoreCase) || - adalEx.ErrorCode.Equals("password_required_for_managed_user", StringComparison.OrdinalIgnoreCase) || - adalEx is Microsoft.Identity.Client.MsalUiRequiredException) - { - logSink.Log("ERROR REQUESTING TOKEN FROM THE AUTHENTICATION CONTEXT - USER intervention required", TraceEventType.Warning); - // ADAL wants the User to do something,, determine if we are able to see a user - if (promptBehavior == PromptBehavior.Always || promptBehavior == PromptBehavior.Auto) - { - // Switch to MFA user mode.. - Microsoft.Identity.Client.IAccount user = null; //TODO:UPDATE THIS OR REMOVE AS WE DETERMIN HOW TO SOLVE THIS ISSUE IN MSAL // new Microsoft.Identity.Client.AccountId(); - user = null; - //user = new UserIdentifier(clientCredentials.UserName.UserName, UserIdentifierType.OptionalDisplayableId); - return await ExecuteAuthenticateServiceProcessAsync(serviceUrl, null, userCert, clientId, redirectUri, promptBehavior, isOnPrem, authority, msalAuthClient, logSink, useDefaultCreds: useDefaultCreds, user: user).ConfigureAwait(false); - } - else - { - logSink.Log("ERROR REQUESTING TOKEN FROM THE AUTHENTICATION CONTEXT - USER intervention required but not permitted by prompt behavior", TraceEventType.Error, adalEx); - throw adalEx; - } - } - else - { - logSink.Log("ERROR REQUESTING Token FROM THE Authentication context - General ADAL Error", TraceEventType.Error, adalEx); - throw adalEx; - } - } - } + var authenticateHeader = rslt.Headers.GetValues("WWW-Authenticate").FirstOrDefault(); + authenticateHeader = authenticateHeader.Trim(); + + // This also checks for cases like "BearerXXXX authorization_uri=...." and "Bearer" and "Bearer " + if (!authenticateHeader.StartsWith(Bearer, StringComparison.OrdinalIgnoreCase) + || authenticateHeader.Length < Bearer.Length + 2 + || !char.IsWhiteSpace(authenticateHeader[Bearer.Length])) + { + //var ex = new ArgumentException(AdalErrorMessage.InvalidAuthenticateHeaderFormat, + // nameof(authenticateHeader)); + //CoreLoggerBase.Default.Error(AdalErrorMessage.InvalidAuthenticateHeaderFormat); + //CoreLoggerBase.Default.ErrorPii(ex); + //throw ex; + } + + authenticateHeader = authenticateHeader.Substring(Bearer.Length).Trim(); + + IDictionary authenticateHeaderItems = null; + try + { + authenticateHeaderItems = + EncodingHelper.ParseKeyValueListStrict(authenticateHeader, ',', false, true); + } + catch //(ArgumentException ex) + { + //var newEx = new ArgumentException(AdalErrorMessage.InvalidAuthenticateHeaderFormat, + // nameof(authenticateHeader), ex); + //CoreLoggerBase.Default.Error(AdalErrorMessage.InvalidAuthenticateHeaderFormat); + //CoreLoggerBase.Default.ErrorPii(newEx); + //throw newEx; + } + + if (authenticateHeaderItems != null) + { + string param; + authenticateHeaderItems.TryGetValue(AuthorityKey, out param); + authRoutingProperties.Authority = + param.Replace("oauth2/authorize", "") // swap out the old oAuth pattern. + .Replace("common", "organizations"); // swap common for organizations because MSAL reasons. + authenticateHeaderItems.TryGetValue(ResourceKey, out param); + authRoutingProperties.Resource = param; + } + } + + return authRoutingProperties; + } + + /// + /// Process MSAL exception and provide common handlers. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + private async static Task ProcessMsalExecptionAsync( + Uri serviceUrl, + ClientCredentials clientCredentials, + X509Certificate2 userCert, + string clientId, + Uri redirectUri, + PromptBehavior promptBehavior, + bool isOnPrem, + string authority, + object msalAuthClient, + DataverseTraceLogger logSink, + bool useDefaultCreds, + MemoryBackedTokenCache memoryBackedTokenCache, + string tokenCacheStorePath, + MsalException msalEx) + { + if (msalEx.ErrorCode.Equals("interaction_required", StringComparison.OrdinalIgnoreCase) || + msalEx.ErrorCode.Equals("user_password_expired", StringComparison.OrdinalIgnoreCase) || + msalEx.ErrorCode.Equals("password_required_for_managed_user", StringComparison.OrdinalIgnoreCase) || + msalEx is Microsoft.Identity.Client.MsalUiRequiredException) + { + logSink.Log("ERROR REQUESTING TOKEN FROM THE AUTHENTICATION CONTEXT - USER intervention required", TraceEventType.Warning); + // MSAL wants the User to do something,, determine if we are able to see a user + if (promptBehavior == PromptBehavior.Always || promptBehavior == PromptBehavior.Auto) + { + // Switch to MFA user mode.. + Microsoft.Identity.Client.IAccount user = null; //TODO:UPDATE THIS OR REMOVE AS WE DETERMIN HOW TO SOLVE THIS ISSUE IN MSAL // new Microsoft.Identity.Client.AccountId(); + user = null; + //user = new UserIdentifier(clientCredentials.UserName.UserName, UserIdentifierType.OptionalDisplayableId); + return await ExecuteAuthenticateServiceProcessAsync(serviceUrl, null, userCert, clientId, redirectUri, promptBehavior, isOnPrem, authority, msalAuthClient, logSink, useDefaultCreds: useDefaultCreds, user: user, memoryBackedTokenCache: memoryBackedTokenCache, tokenCacheStorePath: tokenCacheStorePath).ConfigureAwait(false); + } + else + { + logSink.Log("ERROR REQUESTING TOKEN FROM THE AUTHENTICATION CONTEXT - USER intervention required but not permitted by prompt behavior", TraceEventType.Error, msalEx); + throw msalEx; + } + } + else + { + logSink.Log("ERROR REQUESTING Token FROM THE Authentication context - General MSAL Error", TraceEventType.Error, msalEx); + throw msalEx; + } + } + } } diff --git a/src/GeneralTools/DataverseClient/Client/Auth/ExecuteAuthenticationResults.cs b/src/GeneralTools/DataverseClient/Client/Auth/ExecuteAuthenticationResults.cs index ce62f0a..7fd5ce7 100644 --- a/src/GeneralTools/DataverseClient/Client/Auth/ExecuteAuthenticationResults.cs +++ b/src/GeneralTools/DataverseClient/Client/Auth/ExecuteAuthenticationResults.cs @@ -1,4 +1,5 @@ using Microsoft.Identity.Client; +using Microsoft.PowerPlatform.Dataverse.Client.Auth.TokenCache; using System; using System.Collections.Generic; using System.Linq; @@ -8,7 +9,7 @@ namespace Microsoft.PowerPlatform.Dataverse.Client.Auth { /// - /// Class used to describe the outcome of the execute authentication process. + /// Class used to describe the outcome of the execute authentication process. /// internal class ExecuteAuthenticationResults { @@ -17,10 +18,11 @@ internal class ExecuteAuthenticationResults public object MsalAuthClient { get; set; } public string Authority { get; set; } public string Resource { get; set; } - public IAccount UserIdent { get; set; } + public IAccount UserIdent { get; set; } + public MemoryBackedTokenCache MemTokenCache { get; set; } - internal string GetAuthTokenAndProperties ( out AuthenticationResult msalAuthResult, out Uri targetServiceUrl , out object msalAuthClient , out string authority, out string resource , out IAccount userIdent) + internal string GetAuthTokenAndProperties(out AuthenticationResult msalAuthResult, out Uri targetServiceUrl, out object msalAuthClient, out string authority, out string resource, out IAccount userIdent, out MemoryBackedTokenCache memoryBackedTokenCache) { msalAuthResult = MsalAuthResult; targetServiceUrl = TargetServiceUrl; @@ -28,8 +30,9 @@ internal string GetAuthTokenAndProperties ( out AuthenticationResult msalAuthRes authority = Authority; resource = Resource; userIdent = UserIdent; + memoryBackedTokenCache = MemTokenCache; - return MsalAuthResult.AccessToken; + return MsalAuthResult.AccessToken; } } } diff --git a/src/GeneralTools/DataverseClient/Client/Auth/MSALHttpClient.cs b/src/GeneralTools/DataverseClient/Client/Auth/MSALHttpClient.cs new file mode 100644 index 0000000..7d3d6ae --- /dev/null +++ b/src/GeneralTools/DataverseClient/Client/Auth/MSALHttpClient.cs @@ -0,0 +1,20 @@ +using Microsoft.Identity.Client; +using Microsoft.PowerPlatform.Dataverse.Client.Utils; +using Microsoft.Extensions.DependencyInjection; +using System.Net.Http; + +namespace Microsoft.PowerPlatform.Dataverse.Client.Auth +{ + internal class MSALHttpClientFactory : IMsalHttpClientFactory + { + /// + /// Return the HTTP client for MSAL. + /// + /// + public HttpClient GetHttpClient() + { + HttpClient msalClient = ClientServiceProviders.Instance.GetService().CreateClient("MSALClientFactory"); + return msalClient; + } + } +} diff --git a/src/GeneralTools/DataverseClient/Client/Auth/MSALHttpHelper.cs b/src/GeneralTools/DataverseClient/Client/Auth/MSALHttpHelper.cs new file mode 100644 index 0000000..1288d8f --- /dev/null +++ b/src/GeneralTools/DataverseClient/Client/Auth/MSALHttpHelper.cs @@ -0,0 +1,52 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.PowerPlatform.Dataverse.Client.Model; +using Microsoft.PowerPlatform.Dataverse.Client.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.PowerPlatform.Dataverse.Client.Auth +{ + internal class MSALHttpRetryHandlerHelper : DelegatingHandler + { + private readonly int MaxRetryCount = ClientServiceProviders.Instance.GetService>().Value.MsalRetryCount; + + /// + /// Handel Failure and retry + /// + /// + /// + /// + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + HttpResponseMessage response = null; + for (int i = 0; i < MaxRetryCount; i++) + { + response = await base.SendAsync(request, cancellationToken); + if (response.IsSuccessStatusCode) + { + return response; + } + else + { + if (response.StatusCode != System.Net.HttpStatusCode.RequestTimeout) + { + break; + } + else + { +#if DEBUG + System.Diagnostics.Trace.WriteLine($">>> MSAL RETRY ON TIMEOUT >>> {Thread.CurrentThread.ManagedThreadId} - {i}"); +#endif + } + } + } + return response; + } + } +} diff --git a/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/FileBackedTokenCache.cs b/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/FileBackedTokenCache.cs new file mode 100644 index 0000000..6d881d7 --- /dev/null +++ b/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/FileBackedTokenCache.cs @@ -0,0 +1,51 @@ +using Microsoft.Identity.Client; +using Microsoft.Identity.Client.Extensions.Msal; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.PowerPlatform.Dataverse.Client.Auth.TokenCache +{ + /// + /// File backed storage of MSAL tokens for Public Client only. + /// + internal class FileBackedTokenCache + { + private StorageCreationProperties _storageProps = null; + + public FileBackedTokenCache(FileBackedTokenCacheHints cacheOptions) + { + StorageCreationPropertiesBuilder _builder = null; + + _builder = new StorageCreationPropertiesBuilder( + cacheOptions.cacheFileName, cacheOptions.cacheFileDirectory) + .WithLinuxKeyring(cacheOptions.linuxSchemaName, cacheOptions.linuxCollection, cacheOptions.linuxLabel, cacheOptions.linuxAttr1, cacheOptions.linuxAttr2) + .WithMacKeyChain(cacheOptions.macKeyChainServiceName, cacheOptions.macKeyChainServiceAccount); + + _storageProps = _builder.Build(); + } + + /// + /// Initialize and configure the file token cache + /// + /// + /// + public async Task Initialize(ITokenCache tokenCache) + { + if (tokenCache == null) + { + throw new ArgumentNullException(nameof(tokenCache)); + } + + if (tokenCache == null) + { + throw new ArgumentNullException(nameof(_storageProps)); + } + + var cacheHelper = await MsalCacheHelper.CreateAsync(_storageProps).ConfigureAwait(false); + cacheHelper.RegisterCache(tokenCache); + } + } +} diff --git a/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/FileBackedTokenCacheHints.cs b/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/FileBackedTokenCacheHints.cs new file mode 100644 index 0000000..e7ce61f --- /dev/null +++ b/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/FileBackedTokenCacheHints.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Identity.Client.Extensions.Msal; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Reflection; +using Microsoft.Xrm.Sdk.Organization; +using System.Diagnostics; + +namespace Microsoft.PowerPlatform.Dataverse.Client.Auth.TokenCache +{ + internal class FileBackedTokenCacheHints + { + public string cacheFileName { get; set; } + public string cacheFileDirectory { get; set; } + + // Linux KeyRing + public string linuxSchemaName { get; set; } + public string linuxCollection { get; set; } + public string linuxLabel { get; set; } + public KeyValuePair linuxAttr1 { get; set; } + public KeyValuePair linuxAttr2 { get; set; } + + // MAC KeyRing + public string macKeyChainServiceName { get; set; } + public string macKeyChainServiceAccount { get; set; } + + /// + /// Setup File Backed Token Storage with token path. + /// + /// + public FileBackedTokenCacheHints(string tokenPathAndFileName) + { + string hostName = "DvBaseClient"; + if (AppDomain.CurrentDomain != null) + { + hostName = Path.GetFileNameWithoutExtension(AppDomain.CurrentDomain.FriendlyName); + } + string hostVersion = typeof(OrganizationDetail).Assembly.GetCustomAttribute().Version ?? FileVersionInfo.GetVersionInfo(typeof(OrganizationDetail).Assembly.Location).FileVersion; + string companyName = typeof(OrganizationDetail).Assembly.GetCustomAttribute().Company; + + + if (string.IsNullOrEmpty(tokenPathAndFileName)) + { + tokenPathAndFileName = Path.Combine(MsalCacheHelper.UserRootDirectory, companyName?.Replace(" ", "_"), hostName, hostVersion, "dvtokens.dat"); + } + + System.Diagnostics.Trace.WriteLine($"TokenCacheFilePath: {tokenPathAndFileName}"); + + cacheFileDirectory = Path.GetDirectoryName(tokenPathAndFileName); + cacheFileName = Path.GetFileName(tokenPathAndFileName); + + // configure MAC properties: + macKeyChainServiceName = $"{hostName}_service"; + macKeyChainServiceAccount = $"{hostName}_account"; + + // configure LinuxKeys + linuxSchemaName = $"{hostName}.dvserviceclient.tokencache"; + linuxCollection = MsalCacheHelper.LinuxKeyRingDefaultCollection; + linuxLabel = $"Token Storage for {hostName}"; + linuxAttr1 = new KeyValuePair("Version", string.IsNullOrEmpty(hostVersion) ? "1.0" : hostVersion); + linuxAttr2 = new KeyValuePair("ProductGroup", hostName); + } + } +} diff --git a/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/MemoryBackedTokenCache.cs b/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/MemoryBackedTokenCache.cs new file mode 100644 index 0000000..d08d689 --- /dev/null +++ b/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/MemoryBackedTokenCache.cs @@ -0,0 +1,158 @@ +using Microsoft.Identity.Client; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; +using System; + +// Adapted from Microsoft.Identity.Web/TokenCacheProviders In Memory Token cacheProvider files for use by clients. +// https://github.com/AzureAD/microsoft-identity-web/blob/master/src/Microsoft.Identity.Web/TokenCacheProviders/MsalAbstractTokenCacheProvider.cs +// and +// https://github.com/AzureAD/microsoft-identity-web/blob/master/src/Microsoft.Identity.Web/TokenCacheProviders/InMemory/MsalMemoryTokenCacheProvider.cs +namespace Microsoft.PowerPlatform.Dataverse.Client.Auth.TokenCache +{ + + /// + /// DV Client On-board memory cache system for tokens. + /// + internal class MemoryBackedTokenCache + { + /// + /// .NET Core Memory cache. + /// + private readonly IMemoryCache _memoryCache; + + /// + /// MSAL memory token cache options. + /// + private readonly MemoryTokenCacheOptions _tokenCacheOptions; + + /// + /// Default constructor. + /// + public MemoryBackedTokenCache(MemoryTokenCacheOptions tokenCacheOptions) + { + _tokenCacheOptions = tokenCacheOptions; + _memoryCache = new MemoryCache(new MemoryCacheOptions()); + } + + /// + /// Initializes the token cache serialization. + /// + /// Token cache to serialize/deserialize. + public void Initialize(ITokenCache tokenCache) + { + if (tokenCache == null) + { + throw new ArgumentNullException(nameof(tokenCache)); + } + + tokenCache.SetBeforeAccessAsync(OnBeforeAccessAsync); + tokenCache.SetAfterAccessAsync(OnAfterAccessAsync); + } + + /// + /// Clean up token cache. + /// + public void ClearCache() + { + _memoryCache.Dispose(); + } + + private async Task OnBeforeAccessAsync(TokenCacheNotificationArgs args) + { + if (!string.IsNullOrEmpty(args.SuggestedCacheKey)) + { + byte[] tokenCacheBytes = await ReadCacheBytesAsync(args.SuggestedCacheKey).ConfigureAwait(false); + + //args.TokenCache.DeserializeMsalV3(UnprotectBytes(tokenCacheBytes), shouldClearExistingCache: true); + args.TokenCache.DeserializeMsalV3(tokenCacheBytes, shouldClearExistingCache: true); + } + } + + + private async Task OnAfterAccessAsync(TokenCacheNotificationArgs args) + { + // The access operation resulted in a cache update. + if (args.HasStateChanged) + { + MemoryCacheSerializerHints cacheSerializerHints = CreateHintsFromArgs(args); + + if (args.HasTokens) + { + await WriteCacheBytesAsync(args.SuggestedCacheKey, args.TokenCache.SerializeMsalV3(), cacheSerializerHints).ConfigureAwait(false); + } + else + { + // No token in the cache. we can remove the cache entry + await RemoveKeyAsync(args.SuggestedCacheKey).ConfigureAwait(false); + } + } + } + + + #region Utilities + + #endregion + + /// + /// Removes a token cache identified by its key, from the serialization + /// cache. + /// + /// token cache key. + /// A that completes when key removal has completed. + protected Task RemoveKeyAsync(string cacheKey) + { + _memoryCache.Remove(cacheKey); + return Task.CompletedTask; + } + + /// + /// Reads a blob from the serialization cache (identified by its key). + /// + /// Token cache key. + /// Read Bytes. + protected Task ReadCacheBytesAsync(string cacheKey) + { + byte[] tokenCacheBytes = (byte[])_memoryCache.Get(cacheKey); + return Task.FromResult(tokenCacheBytes); + } + + /// + /// Writes a token cache blob to the serialization cache (identified by its key). + /// + /// Token cache key. + /// Bytes to write. + /// Hints for the cache serialization implementation optimization. + /// A that completes when a write operation has completed. + protected Task WriteCacheBytesAsync( + string cacheKey, + byte[] bytes, + MemoryCacheSerializerHints cacheSerializerHints) + { + TimeSpan? cacheExpiry = null; + if (cacheSerializerHints != null && cacheSerializerHints?.SuggestedCacheExpiry != null) + { + cacheExpiry = cacheSerializerHints.SuggestedCacheExpiry.Value.UtcDateTime - DateTime.UtcNow; + if (cacheExpiry < TimeSpan.FromTicks(0)) + { + System.Diagnostics.Trace.WriteLine($"<<<<<<< Bad Calculation detected. Suggested ExpireTime {cacheSerializerHints.SuggestedCacheExpiry.Value.UtcDateTime} - UTC {DateTime.UtcNow} = {cacheExpiry} - Reseting to 1 hrs."); + // CacheExpirey is set in the past, reset to current + cacheExpiry = TimeSpan.FromHours(1); + } + } + + MemoryCacheEntryOptions memoryCacheEntryOptions = new MemoryCacheEntryOptions() + { + AbsoluteExpirationRelativeToNow = cacheExpiry ?? _tokenCacheOptions.AbsoluteExpirationRelativeToNow, + Size = bytes?.Length, + }; + + + _memoryCache.Set(cacheKey, bytes, memoryCacheEntryOptions); + return Task.CompletedTask; + } + + private static MemoryCacheSerializerHints CreateHintsFromArgs(TokenCacheNotificationArgs args) => new MemoryCacheSerializerHints { CancellationToken = args.CancellationToken, SuggestedCacheExpiry = args.SuggestedCacheExpiry }; + + } +} diff --git a/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/MemoryCacheSerializerHints.cs b/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/MemoryCacheSerializerHints.cs new file mode 100644 index 0000000..b877be2 --- /dev/null +++ b/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/MemoryCacheSerializerHints.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +// Adapted from Microsoft.Identity.Web/TokenCacheProviders/CacheSerializerHints.cs for general use. +// https://github.com/AzureAD/microsoft-identity-web/blob/master/src/Microsoft.Identity.Web/TokenCacheProviders/CacheSerializerHints.cs + +namespace Microsoft.PowerPlatform.Dataverse.Client.Auth.TokenCache +{ + /// + /// Set of properties that the token cache serialization implementations might use to optimize the cache. + /// + internal class MemoryCacheSerializerHints + { + /// + /// CancellationToken enabling cooperative cancellation between threads, thread pool, or Task objects. + /// + public CancellationToken CancellationToken { get; set; } + + /// + /// Suggested cache expiry based on the in-coming token. Use to optimize cache eviction + /// with the app token cache. + /// + public DateTimeOffset? SuggestedCacheExpiry { get; set; } + } +} diff --git a/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/MemoryTokenCacheOptions.cs b/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/MemoryTokenCacheOptions.cs new file mode 100644 index 0000000..469c413 --- /dev/null +++ b/src/GeneralTools/DataverseClient/Client/Auth/TokenCache/MemoryTokenCacheOptions.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.PowerPlatform.Dataverse.Client.Auth.TokenCache +{ + internal class MemoryTokenCacheOptions + { + /// By default, the sliding expiration is set for 14 days + public MemoryTokenCacheOptions() + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(14); + } + + /// + /// Gets or sets the value of the duration after which the cache entry will expire unless it's used + /// This is the duration the tokens are kept in memory cache. + /// In production, a higher value, up-to 90 days is recommended. + /// + /// + /// The AbsoluteExpirationRelativeToNow value. + /// + public TimeSpan AbsoluteExpirationRelativeToNow + { + get; + set; + } + } +} diff --git a/src/GeneralTools/DataverseClient/Client/Client/OrganizationWebProxyClientAsync.cs b/src/GeneralTools/DataverseClient/Client/Client/OrganizationWebProxyClientAsync.cs new file mode 100644 index 0000000..3bb5a61 --- /dev/null +++ b/src/GeneralTools/DataverseClient/Client/Client/OrganizationWebProxyClientAsync.cs @@ -0,0 +1,237 @@ +namespace Microsoft.Xrm.Sdk.WebServiceClient +{ + using System; + using System.Diagnostics.CodeAnalysis; + using System.Reflection; + using System.Threading.Tasks; + using Microsoft.PowerPlatform.Dataverse.Client; + using Query; + +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + + internal class OrganizationWebProxyClientAsync : WebProxyClient, IOrganizationServiceAsync + { + public OrganizationWebProxyClientAsync(Uri serviceUrl, bool useStrongTypes) + : base(serviceUrl, useStrongTypes) + { + } + + public OrganizationWebProxyClientAsync(Uri serviceUrl, Assembly strongTypeAssembly) + : base(serviceUrl, strongTypeAssembly) + { + } + + public OrganizationWebProxyClientAsync(Uri serviceUrl, TimeSpan timeout, bool useStrongTypes) + : base(serviceUrl, timeout, useStrongTypes) + { + } + + public OrganizationWebProxyClientAsync(Uri uri, TimeSpan timeout, Assembly strongTypeAssembly) + : base(uri, timeout, strongTypeAssembly) + { + } + + #region Properties + + internal bool OfflinePlayback { get; set; } + + public string SyncOperationType { get; set; } + + public Guid CallerId { get; set; } + + public UserType userType { get; set; } + + public Guid CallerRegardingObjectId { get; set; } + + internal int LanguageCodeOverride { get; set; } + + #endregion + + #region IOrganizationService implementation + + public void Associate(string entityName, Guid entityId, Relationship relationship, + EntityReferenceCollection relatedEntities) + { + AssociateCore(entityName, entityId, relationship, relatedEntities); + } + + public Task AssociateAsync(string entityName, Guid entityId, Relationship relationship, + EntityReferenceCollection relatedEntities) + { + return AssociateAsyncCore(entityName, entityId, relationship, relatedEntities); + } + + public Guid Create(Entity entity) + { + return CreateCore(entity); + } + + public Task CreateAsync(Entity entity) + { + return CreateAsyncCore(entity); + } + + public void Delete(string entityName, Guid id) + { + DeleteCore(entityName, id); + } + + public Task DeleteAsync(string entityName, Guid id) + { + return DeleteAsyncCore(entityName, id); + } + + public void Disassociate(string entityName, Guid entityId, Relationship relationship, + EntityReferenceCollection relatedEntities) + { + DisassociateCore(entityName, entityId, relationship, relatedEntities); + } + + public Task DisassociateAsync(string entityName, Guid entityId, Relationship relationship, + EntityReferenceCollection relatedEntities) + { + return DisassociateAsyncCore(entityName, entityId, relationship, relatedEntities); + } + + public OrganizationResponse Execute(OrganizationRequest request) + { + return ExecuteCore(request); + } + + public Task ExecuteAsync(OrganizationRequest request) + { + return ExecuteAsyncCore(request); + } + + public Entity Retrieve(string entityName, Guid id, ColumnSet columnSet) + { + return RetrieveCore(entityName, id, columnSet); + } + + public Task RetrieveAsync(string entityName, Guid id, ColumnSet columnSet) + { + return RetrieveAsyncCore(entityName, id, columnSet); + } + + public EntityCollection RetrieveMultiple(QueryBase query) + { + return RetrieveMultipleCore(query); + } + + public Task RetrieveMultipleAsync(QueryBase query) + { + return RetrieveMultipleAsyncCore(query); + } + + public void Update(Entity entity) + { + UpdateCore(entity); + } + + public Task UpdateAsync(Entity entity) + { + return UpdateAsyncCore(entity); + } + + #endregion + + #region Protected IOrganizationService CoreMembers + + protected internal virtual Guid CreateCore(Entity entity) + { + return ExecuteAction(() => Channel.Create(entity)); + } + + protected Task CreateAsyncCore(Entity entity) + { + return ExecuteAction(() => Channel.CreateAsync(entity)); + } + + protected internal virtual Entity RetrieveCore(string entityName, Guid id, ColumnSet columnSet) + { + return ExecuteAction(() => Channel.Retrieve(entityName, id, columnSet)); + } + + protected internal virtual Task RetrieveAsyncCore(string entityName, Guid id, ColumnSet columnSet) + { + return ExecuteAction(() => Channel.RetrieveAsync(entityName, id, columnSet)); + } + + protected internal virtual void UpdateCore(Entity entity) + { + ExecuteAction(() => Channel.Update(entity)); + } + + protected internal virtual Task UpdateAsyncCore(Entity entity) + { + return ExecuteAction(() => Channel.UpdateAsync(entity)); + } + + protected internal virtual void DeleteCore(string entityName, Guid id) + { + ExecuteAction(() => Channel.Delete(entityName, id)); + } + + protected internal virtual Task DeleteAsyncCore(string entityName, Guid id) + { + return ExecuteAction(() => Channel.DeleteAsync(entityName, id)); + } + + protected internal virtual OrganizationResponse ExecuteCore(OrganizationRequest request) + { + return ExecuteAction(() => Channel.Execute(request)); + } + + protected internal virtual Task ExecuteAsyncCore(OrganizationRequest request) + { + return ExecuteAction(() => Channel.ExecuteAsync(request)); + } + + protected internal virtual void AssociateCore(string entityName, Guid entityId, Relationship relationship, + EntityReferenceCollection relatedEntities) + { + ExecuteAction(() => Channel.Associate(entityName, entityId, relationship, relatedEntities)); + } + + protected internal virtual Task AssociateAsyncCore(string entityName, Guid entityId, Relationship relationship, + EntityReferenceCollection relatedEntities) + { + return ExecuteAction(() => Channel.AssociateAsync(entityName, entityId, relationship, relatedEntities)); + } + + protected internal virtual void DisassociateCore(string entityName, Guid entityId, Relationship relationship, + EntityReferenceCollection relatedEntities) + { + ExecuteAction(() => Channel.Disassociate(entityName, entityId, relationship, relatedEntities)); + } + + protected internal virtual Task DisassociateAsyncCore(string entityName, Guid entityId, Relationship relationship, + EntityReferenceCollection relatedEntities) + { + return ExecuteAction(() => Channel.DisassociateAsync(entityName, entityId, relationship, relatedEntities)); + } + + protected internal virtual EntityCollection RetrieveMultipleCore(QueryBase query) + { + return ExecuteAction(() => Channel.RetrieveMultiple(query)); + } + + protected internal virtual Task RetrieveMultipleAsyncCore(QueryBase query) + { + return ExecuteAction(() => Channel.RetrieveMultipleAsync(query)); + } + + #endregion Protected Members + + #region Protected Methods + + protected override WebProxyClientContextInitializer CreateNewInitializer() + { + return new OrganizationWebProxyClientAsyncContextInitializer(this); + } + + #endregion + } +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + +} diff --git a/src/GeneralTools/DataverseClient/Client/Client/OrganizationWebProxyClientContextInitializer.cs b/src/GeneralTools/DataverseClient/Client/Client/OrganizationWebProxyClientContextInitializer.cs new file mode 100644 index 0000000..92c70c9 --- /dev/null +++ b/src/GeneralTools/DataverseClient/Client/Client/OrganizationWebProxyClientContextInitializer.cs @@ -0,0 +1,88 @@ +namespace Microsoft.Xrm.Sdk.WebServiceClient +{ + using Client; + using Microsoft.PowerPlatform.Dataverse.Client; + using System; + using System.ServiceModel; + using System.ServiceModel.Channels; + using XmlNamespaces; + + /// + /// Manages context for sdk calls + /// + internal sealed class OrganizationWebProxyClientAsyncContextInitializer : + WebProxyClientContextInitializer + { + public OrganizationWebProxyClientAsyncContextInitializer(OrganizationWebProxyClientAsync proxy) + : base(proxy) + { + Initialize(); + } + + #region Properties + + private OrganizationWebProxyClientAsync OrganizationWebProxyClient + { + get { return ServiceProxy as OrganizationWebProxyClientAsync; } + } + + #endregion + + #region Private Methods + + private void Initialize() + { + if (ServiceProxy == null) + { + return; + } + + AddTokenToHeaders(); + + if (ServiceProxy != null) + { + if (OrganizationWebProxyClient.OfflinePlayback) + { + OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader(SdkHeaders.IsOfflinePlayback, + V5.Contracts, true)); + } + + if (OrganizationWebProxyClient.CallerId != Guid.Empty) + { + OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader(SdkHeaders.CallerId, + V5.Contracts, + OrganizationWebProxyClient.CallerId)); + } + + if (OrganizationWebProxyClient.CallerRegardingObjectId != Guid.Empty) + { + OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader(SdkHeaders.CallerRegardingObjectId, + V5.Contracts, + OrganizationWebProxyClient.CallerRegardingObjectId)); + } + + if (OrganizationWebProxyClient.LanguageCodeOverride != 0) + { + OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader(SdkHeaders.LanguageCodeOverride, + V5.Contracts, + OrganizationWebProxyClient.LanguageCodeOverride)); + } + + if (OrganizationWebProxyClient.SyncOperationType != null) + { + OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader(SdkHeaders.OutlookSyncOperationType, + V5.Contracts, + OrganizationWebProxyClient.SyncOperationType)); + } + + OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader(SdkHeaders.UserType, + V5.Contracts, + OrganizationWebProxyClient.userType)); + + AddCommonHeaders(); + } + } + + #endregion + } +} diff --git a/src/GeneralTools/DataverseClient/Client/ConnectionService.cs b/src/GeneralTools/DataverseClient/Client/ConnectionService.cs index 7f7b07e..6555109 100644 --- a/src/GeneralTools/DataverseClient/Client/ConnectionService.cs +++ b/src/GeneralTools/DataverseClient/Client/ConnectionService.cs @@ -1,10 +1,12 @@ #region using using Microsoft.Crm.Sdk.Messages; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Identity.Client; using Microsoft.PowerPlatform.Dataverse.Client.Auth; +using Microsoft.PowerPlatform.Dataverse.Client.Auth.TokenCache; using Microsoft.PowerPlatform.Dataverse.Client.Model; using Microsoft.PowerPlatform.Dataverse.Client.Utils; using Microsoft.Rest; @@ -72,8 +74,8 @@ internal sealed class ConnectionService : IConnectionService, IDisposable { #region variables [NonSerializedAttribute] - private OrganizationWebProxyClient _svcWebClientProxy; - private OrganizationWebProxyClient _externalWebClientProxy; // OAuth specific web service proxy + private OrganizationWebProxyClientAsync _svcWebClientProxy; + private OrganizationWebProxyClientAsync _externalWebClientProxy; // OAuth specific web service proxy [NonSerializedAttribute] private WhoAmIResponse user; // Dataverse user entity that is the service. @@ -101,6 +103,7 @@ internal sealed class ConnectionService : IConnectionService, IDisposable private SecureString _LivePass; private string _DataverseOnlineRegion; // Region of Dataverse Online to use. private string _ServiceCACHEName = "Microsoft.PowerPlatform.Dataverse.Client.Service"; // this is the base cache key name that will be used to cache the service. + private static readonly IMemoryCache _clientMemoryCache = new MemoryCache(new MemoryCacheOptions()); // InMemory Cache for the Local dataverse Client. //OAuth Params private PromptBehavior _promptBehavior; // prompt behavior @@ -250,8 +253,25 @@ internal sealed class ConnectionService : IConnectionService, IDisposable /// internal IAccount _userAccount = null; + /// + /// Lock object used to control access to the acquire token flows when in clone mode. + /// MSAL does not appear to be threadsafe when multiple threads attempt to access the same MSAL object. + /// + internal readonly static object lockObject = new object(); + + /// + /// MemoryBacked Token cache object. + /// Used for Confidential clients primarily. + /// + internal MemoryBackedTokenCache _memoryBackedTokenCache = null; + // ********* End MSAL Properties ******** + /// + /// Memory cache for current instance. + /// + internal IMemoryCache LocalMemoryCache { get { return _clientMemoryCache; } } + /// /// When true, indicates the construction is coming from a clone process. @@ -373,13 +393,13 @@ internal AuthenticationType AuthenticationTypeInUse /// /// Returns the Dataverse Web Client /// - internal OrganizationWebProxyClient WebClient + internal OrganizationWebProxyClientAsync WebClient { get { if (_svcWebClientProxy != null) { - RefreshWebProxyClientTokenAsync().GetAwaiter().GetResult(); // Only call this if the connection is not null + RefreshClientTokenAsync().ConfigureAwait(false).GetAwaiter().GetResult(); // Only call this if the connection is not null try { if (!_svcWebClientProxy.Endpoint.EndpointBehaviors.Contains(typeof(DataverseTelemetryBehaviors))) @@ -524,6 +544,14 @@ internal string EnvironmentId /// internal Func> GetAccessTokenAsync { get; set; } + + //TODO: COMPLETE EXPOSURE OF ADDITIONAL HEADER FEATURE + /// + /// Function to call to get the current headers for this request + /// + internal Func>> RequestAdditionalHeadersAsync { get; set; } + + /// /// returns the format string for the baseWebAPI /// @@ -548,6 +576,11 @@ internal bool EnableCookieRelay set { _enableCookieRelay = value; } } + /// + /// Cookies that are being passed though clients, when cookies are used + /// + internal Dictionary CurrentCookieCollection { get; set; } = null; + /// /// Value used by the retry system while the code is running, /// this value can scale up and down based on throttling limits. @@ -565,11 +598,17 @@ internal bool EnableCookieRelay /// TEST Support Constructor. /// /// - internal ConnectionService(IOrganizationService testIOrganziationSvc) + /// + /// + /// + internal ConnectionService(IOrganizationService testIOrganziationSvc , string baseConnectUrl, HttpClient mockClient, ILogger logger) { _testSupportIOrg = testIOrganziationSvc; - logEntry = new DataverseTraceLogger(); + WebApiHttpClient = mockClient; + logEntry = new DataverseTraceLogger(logger); isLogEntryCreatedLocaly = true; + ConnectedOrgPublishedEndpoints = new EndpointCollection(); + ConnectedOrgPublishedEndpoints.Add(EndpointType.OrganizationDataService, baseConnectUrl); RefreshInstanceDetails(testIOrganziationSvc, null).ConfigureAwait(false).GetAwaiter().GetResult(); } @@ -578,7 +617,7 @@ internal ConnectionService(IOrganizationService testIOrganziationSvc) /// /// This is an initialized organization web Service proxy /// incoming Log Sink - internal ConnectionService(OrganizationWebProxyClient externalOrgWebProxyClient, DataverseTraceLogger logSink = null) + internal ConnectionService(OrganizationWebProxyClientAsync externalOrgWebProxyClient, DataverseTraceLogger logSink = null) { if (logSink == null) { @@ -614,9 +653,9 @@ internal ConnectionService(OrganizationWebProxyClient externalOrgWebProxyClient, /// Organization to Connect too /// Live Password to use /// Live ID to use - /// CrmOnlineRegion - /// flag that will tell the instance to create a Unique Name for the CRM Cache Objects. - /// Dataverse Org Detail object, this is is returned from a query to the CRM Discovery Server service. not required. + /// OnlineRegion + /// flag that will tell the instance to create a Unique Name for the connection Cache Objects. + /// Dataverse Org Detail object, this is is returned from a query to the Discovery Server service. not required. /// Client Id of the registered application. /// RedirectUri for the application redirecting to /// Whether to prompt when no username/password @@ -626,23 +665,25 @@ internal ConnectionService(OrganizationWebProxyClient externalOrgWebProxyClient, /// Incoming Log Provide /// Targeted Instance to connector too. /// (optional) If true attempts login using current user ( Online ) + /// (optional) if provided sets the path to write the token cache too. internal ConnectionService( AuthenticationType authType, // Only OAuth is supported in this constructor. - string orgName, // CRM Organization Name your connecting too + string orgName, // Organization Name your connecting too string liveUserId, // Live ID - Live only SecureString livePass, // Live Pw - Live Only - string crmOnlineRegion, + string onlineRegion, bool useUniqueCacheName, // tells the system to create a unique cache name for this instance. OrganizationDetail orgDetail, string clientId, // The client Id of the client registered with Azure Uri redirectUri, // The redirectUri telling the redirect login window - PromptBehavior promptBehavior, // The prompt behavior for ADAL library + PromptBehavior promptBehavior, // The prompt behavior for MSAL library string hostName, // Host name to connect to string port, // Port used to connect to bool onPrem, DataverseTraceLogger logSink = null, Uri instanceToConnectToo = null, - bool useDefaultCreds = false + bool useDefaultCreds = false, + string tokenCacheStorePath = null ) { if (authType != AuthenticationType.OAuth && authType != AuthenticationType.ClientSecret) @@ -664,12 +705,12 @@ internal ConnectionService( _organization = orgName; _LiveID = liveUserId; _LivePass = livePass; - _DataverseOnlineRegion = crmOnlineRegion; + _DataverseOnlineRegion = onlineRegion; _OrgDetail = orgDetail; _clientId = clientId; _redirectUri = redirectUri; _promptBehavior = promptBehavior; - _tokenCachePath = string.Empty; //TODO: Remove / Replace + _tokenCachePath = tokenCacheStorePath; _hostname = hostName; _port = port; _isOnPremOAuth = onPrem; @@ -682,8 +723,8 @@ internal ConnectionService( /// Sets up and initializes the Dataverse Service interface using Certificate Auth. /// /// Only Certificate flows are supported in this constructor - /// flag that will tell the instance to create a Unique Name for the CRM Cache Objects. - /// Dataverse Org Detail object, this is is returned from a query to the CRM Discovery Server service. not required. + /// flag that will tell the instance to create a Unique Name for the Cache Objects. + /// Dataverse Org Detail object, this is is returned from a query to the Discovery Server service. not required. /// Client Id of the registered application. /// RedirectUri for the application redirecting to /// Hostname to connect to @@ -694,6 +735,7 @@ internal ConnectionService( /// Thumb print of the Certificate to use for this connection. /// Direct Instance Uri to Connect To /// Incoming Log Sink data + /// (optional) if provided, sets the path to store or read user tokens internal ConnectionService( AuthenticationType authType, // Only Certificate is supported in this constructor. Uri instanceToConnectToo, // set the connection instance to use. @@ -707,7 +749,8 @@ internal ConnectionService( string hostName, // Host name to connect to string port, // Port used to connect to bool onPrem, - DataverseTraceLogger logSink = null) + DataverseTraceLogger logSink = null, + string tokenCacheStorePath = null) { if (authType != AuthenticationType.Certificate && authType != AuthenticationType.ExternalTokenManagement) throw new ArgumentOutOfRangeException("authType", "This constructor only supports the Certificate Auth type"); @@ -729,7 +772,7 @@ internal ConnectionService( _OrgDetail = orgDetail; _clientId = clientId; _redirectUri = redirectUri; - _tokenCachePath = string.Empty; + _tokenCachePath = tokenCacheStorePath; _hostname = hostName; _port = port; _isOnPremOAuth = onPrem; @@ -778,7 +821,7 @@ private bool IntilizeService(out ConnectionService ConnectionObject) if (dvService != null) { - _svcWebClientProxy = (OrganizationWebProxyClient)dvService; + _svcWebClientProxy = (OrganizationWebProxyClientAsync)dvService; return true; } else @@ -797,9 +840,9 @@ private IOrganizationService GetCachedService(out ConnectionService ConnectionOb { try { - var objProspectiveCachedClient = System.Runtime.Caching.MemoryCache.Default[_ServiceCACHEName]; - if (objProspectiveCachedClient != null && objProspectiveCachedClient is ConnectionService) - ConnectionObject = (ConnectionService)objProspectiveCachedClient; + var objProspectiveCachedClient = _clientMemoryCache.Get(_ServiceCACHEName); + if (objProspectiveCachedClient != null && objProspectiveCachedClient is ConnectionService service) + ConnectionObject = service; else ConnectionObject = null; } @@ -820,10 +863,8 @@ private IOrganizationService GetCachedService(out ConnectionService ConnectionOb if (!string.IsNullOrEmpty(_ServiceCACHEName)) { - if (System.Runtime.Caching.MemoryCache.Default.Contains(_ServiceCACHEName)) - System.Runtime.Caching.MemoryCache.Default.Remove(_ServiceCACHEName); - // Cache the Service for 5 min. - System.Runtime.Caching.MemoryCache.Default.Add(_ServiceCACHEName, this, DateTime.Now.AddMinutes(5)); + // Set or update the cache the Service for 5 min. + _clientMemoryCache.Set(_ServiceCACHEName, this, DateTimeOffset.Now.AddMinutes(5)); } return localSvc; } @@ -907,7 +948,7 @@ private async Task InitServiceAsync() #region AD or SPLA Auth try { - string CrmUrl = string.Empty; + string DvUrl = string.Empty; #region AD if (_OrgDetail == null) { @@ -915,7 +956,7 @@ private async Task InitServiceAsync() if (!string.IsNullOrWhiteSpace(_port)) { // http://:/XRMServices/2011/Discovery.svc?wsdl - CrmUrl = String.Format(CultureInfo.InvariantCulture, + DvUrl = String.Format(CultureInfo.InvariantCulture, "{0}://{1}:{2}/XRMServices/2011/Discovery.svc", _InternetProtocalToUse, _hostname, @@ -923,16 +964,16 @@ private async Task InitServiceAsync() } else { - CrmUrl = String.Format(CultureInfo.InvariantCulture, + DvUrl = String.Format(CultureInfo.InvariantCulture, "{0}://{1}/XRMServices/2011/Discovery.svc", _InternetProtocalToUse, _hostname); } - logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Discovery URI is = {0}", CrmUrl), TraceEventType.Information); - if (!Uri.IsWellFormedUriString(CrmUrl, UriKind.Absolute)) + logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Discovery URI is = {0}", DvUrl), TraceEventType.Information); + if (!Uri.IsWellFormedUriString(DvUrl, UriKind.Absolute)) { // Throw error here. - logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Discovery URI is malformed = {0}", CrmUrl), TraceEventType.Error); + logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Discovery URI is malformed = {0}", DvUrl), TraceEventType.Error); return null; } @@ -973,31 +1014,31 @@ private async Task InitServiceAsync() if (_OrgDetail == null) { // Discover Orgs Url. - Uri uCrmUrl = new Uri(CrmUrl); + Uri uDvUrl = new Uri(DvUrl); // This will try to discover any organizations that the user has access too, one way supports AD / IFD and the other supports Claims - OrganizationDetailCollection orgs = null; + DiscoverOrganizationsResult discoverOrganizationsResult = null; if (_eAuthType == AuthenticationType.OAuth) { - orgs = await DiscoverOrganizationsAsync(uCrmUrl, _UserClientCred, _clientId, _redirectUri, _promptBehavior, true, _authority, logEntry).ConfigureAwait(false); + discoverOrganizationsResult = await DiscoverOrganizationsAsync(uDvUrl, _UserClientCred, _clientId, _redirectUri, _promptBehavior, true, _authority, logSink: logEntry, tokenCacheStorePath: _tokenCachePath).ConfigureAwait(false); } else { if (_eAuthType == AuthenticationType.Certificate) { - orgs = await DiscoverOrganizationsAsync(uCrmUrl, _certificateOfConnection, _clientId, true, _authority, logEntry).ConfigureAwait(false); + discoverOrganizationsResult = await DiscoverOrganizationsAsync(uDvUrl, _certificateOfConnection, _clientId, true, _authority, logEntry, tokenCacheStorePath: _tokenCachePath).ConfigureAwait(false); } } // Check the Result to see if we have Orgs back - if (orgs != null && orgs.Count > 0) + if (discoverOrganizationsResult.OrganizationDetailCollection != null && discoverOrganizationsResult.OrganizationDetailCollection.Count > 0) { - logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Found {0} Org(s)", orgs.Count), TraceEventType.Information); - orgDetail = orgs.FirstOrDefault(o => string.Compare(o.UniqueName, _organization, StringComparison.CurrentCultureIgnoreCase) == 0); + logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Found {0} Org(s)", discoverOrganizationsResult.OrganizationDetailCollection.Count), TraceEventType.Information); + orgDetail = discoverOrganizationsResult.OrganizationDetailCollection.FirstOrDefault(o => string.Compare(o.UniqueName, _organization, StringComparison.CurrentCultureIgnoreCase) == 0); if (orgDetail == null) - orgDetail = orgs.FirstOrDefault(o => string.Compare(o.FriendlyName, _organization, StringComparison.CurrentCultureIgnoreCase) == 0); + orgDetail = discoverOrganizationsResult.OrganizationDetailCollection.FirstOrDefault(o => string.Compare(o.FriendlyName, _organization, StringComparison.CurrentCultureIgnoreCase) == 0); if (orgDetail == null) { @@ -1015,7 +1056,7 @@ private async Task InitServiceAsync() else orgDetail = _OrgDetail; // Assign to passed in value. - // Try to connect to CRM here. + // Try to connect to Dataverse here. dvService = await ConnectAndInitServiceAsync(orgDetail, true, uUserHomeRealm).ConfigureAwait(false); if (dvService == null) @@ -1025,7 +1066,7 @@ private async Task InitServiceAsync() } if (_eAuthType == AuthenticationType.OAuth || _eAuthType == AuthenticationType.Certificate || _eAuthType == AuthenticationType.ClientSecret) - dvService = (OrganizationWebProxyClient)dvService; + dvService = (OrganizationWebProxyClientAsync)dvService; #endregion @@ -1109,7 +1150,7 @@ private async Task InitServiceAsync() dvService = await ConnectAndInitServiceAsync(orgList.OrgsList.First().OrgDetail, false, null).ConfigureAwait(false); if (dvService != null) { - dvService = (OrganizationWebProxyClient)dvService; + dvService = (OrganizationWebProxyClientAsync)dvService; // Update Region _DataverseOnlineRegion = onlineServerList.GetServerShortNameByDisplayName(orgList.OrgsList.First().DiscoveryServerName); @@ -1121,7 +1162,7 @@ private async Task InitServiceAsync() { if (!string.IsNullOrWhiteSpace(_organization)) { - logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Looking for Organization = {0} in the results from CRM's Discovery server list.", _organization), TraceEventType.Information); + logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Looking for Organization = {0} in the results from Discovery server list.", _organization), TraceEventType.Information); // Find the Stored org in the returned collection.. OrgByServer orgDetail = Utilities.DeterminOrgDataFromOrgInfo(orgList, _organization); @@ -1132,7 +1173,7 @@ private async Task InitServiceAsync() dvService = await ConnectAndInitServiceAsync(orgDetail.OrgDetail, false, null).ConfigureAwait(false); if (dvService != null) { - dvService = (OrganizationWebProxyClient)dvService; + dvService = (OrganizationWebProxyClientAsync)dvService; _DataverseOnlineRegion = onlineServerList.GetServerShortNameByDisplayName(orgList.OrgsList.First().DiscoveryServerName); logEntry.Log(string.Format(CultureInfo.InvariantCulture, "User Org ({0}) found in Discovery Server {1}", orgDetail.OrgDetail.UniqueName, _DataverseOnlineRegion)); } @@ -1304,11 +1345,19 @@ private async Task RefreshInstanceDetails(IOrganizationService dvService, Uri ur var request = new RetrieveCurrentOrganizationRequest() { AccessType = 0, RequestId = guRequestId }; RetrieveCurrentOrganizationResponse resp; - - if (_configuration.Value.UseWebApi) + + if (_configuration.Value.UseWebApiLoginFlow) { - resp = (RetrieveCurrentOrganizationResponse)(await Command_WebAPIProcess_ExecuteAsync( - request, null, false, null, Guid.Empty, false, _configuration.Value.MaxRetryCount, _configuration.Value.RetryPauseTime, new CancellationToken(), uriOfInstance).ConfigureAwait(false)); + OrganizationResponse orgResp = await Command_WebAPIProcess_ExecuteAsync( + request, null, false, null, Guid.Empty, false, _configuration.Value.MaxRetryCount, _configuration.Value.RetryPauseTime, new CancellationToken(), uriOfInstance).ConfigureAwait(false); + try + { + resp = (RetrieveCurrentOrganizationResponse)orgResp; + } + catch ( Exception ex ) + { + throw new DataverseOperationException($"Failure to convert OrganziationResponse to requested type - request was {request.RequestName}", ex); + } } else { @@ -1388,7 +1437,7 @@ private async Task RefreshInstanceDetails(IOrganizationService dvService, Uri ur req.RequestId = trackingID; WhoAmIResponse resp; - if (_configuration.Value.UseWebApi) + if (_configuration.Value.UseWebApiLoginFlow) { resp = (WhoAmIResponse)(await Command_WebAPIProcess_ExecuteAsync( req, null, false, null, Guid.Empty, false, _configuration.Value.MaxRetryCount, _configuration.Value.RetryPauseTime, new CancellationToken()).ConfigureAwait(false)); @@ -1436,6 +1485,8 @@ internal void SetClonedProperties(ServiceClient sourceClient) int debugingCloneStateFilter = 0; try { + System.Diagnostics.Trace.WriteLine($"Cloning {sourceClient._connectionSvc._ServiceCACHEName} to create {_ServiceCACHEName}"); + user = sourceClient.SystemUser; debugingCloneStateFilter++; OrganizationVersion = sourceClient._connectionSvc.OrganizationVersion; @@ -1472,6 +1523,8 @@ internal void SetClonedProperties(ServiceClient sourceClient) debugingCloneStateFilter++; _resource = sourceClient._connectionSvc._resource; debugingCloneStateFilter++; + EnableCookieRelay = sourceClient._connectionSvc.EnableCookieRelay; + debugingCloneStateFilter++; } catch (Exception ex) { @@ -1484,7 +1537,7 @@ internal void SetClonedProperties(ServiceClient sourceClient) internal async Task Command_WebAPIProcess_ExecuteAsync(OrganizationRequest req, string logMessageTag, bool bypassPluginExecution, MetadataUtility metadataUtlity, Guid callerId, bool disableConnectionLocking, int maxRetryCount, TimeSpan retryPauseTime, CancellationToken cancellationToken, Uri uriOfInstance = null) { - if (!Utilities.IsRequestValidForTranslationToWebAPI(req, _configuration.Value.UseWebApi)) // THIS WILL GET REMOVED AT SOME POINT, TEMP FOR TRANSTION //TODO:REMOVE ON COMPELTE + if (!Utilities.IsRequestValidForTranslationToWebAPI(req)) // THIS WILL GET REMOVED AT SOME POINT, TEMP FOR TRANSTION //TODO:REMOVE ON COMPELTE { logEntry.Log("Execute Organization Request failed, WebAPI is only supported for limited type of messages at this time.", TraceEventType.Error); return null; @@ -1518,12 +1571,12 @@ internal async Task Command_WebAPIProcess_ExecuteAsync(Org string bodyOfRequest = string.Empty; ExpandoObject requestBodyObject = null; - + if (cReq != null) { - requestBodyObject = Utilities.ToExpandoObject(cReq, metadataUtlity); + requestBodyObject = Utilities.ToExpandoObject(cReq, metadataUtlity, methodToExecute , logEntry); if (cReq.RelatedEntities != null && cReq.RelatedEntities.Count > 0) - requestBodyObject = Utilities.ReleatedEntitiesToExpandoObject(requestBodyObject, cReq.LogicalName, cReq.RelatedEntities, metadataUtlity); + requestBodyObject = Utilities.ReleatedEntitiesToExpandoObject(requestBodyObject, cReq.LogicalName, cReq.RelatedEntities, metadataUtlity, methodToExecute, logEntry); } else { @@ -1704,7 +1757,11 @@ internal async Task Command_WebAPIProcess_ExecuteAsync(Org if (_knownTypesFactory.TryCreate($"{req.RequestName}Response", out var response, json)) { - return (OrganizationResponse)response; + OrganizationResponse resp = (OrganizationResponse)response; + if (string.IsNullOrEmpty(resp.ResponseName)) + resp.ResponseName = $"{req.RequestName}Response"; + + return resp; } var orgResponse = new OrganizationResponse(); @@ -1744,7 +1801,7 @@ internal async Task Command_WebAPIProcess_ExecuteAsync(Org /// uri of instance /// /// - internal async Task Command_WebExecuteAsync(string queryString, string body, HttpMethod method, Dictionary> customHeaders, + internal async Task Command_WebExecuteAsync(string queryString, string body, HttpMethod method, Dictionary> customHeaders, string contentType, string errorStringCheck, Guid callerId, bool disableConnectionLocking, int maxRetryCount, TimeSpan retryPauseTime, Uri uriOfInstance = null, Guid requestTrackingId = default(Guid)) { Stopwatch logDt = new Stopwatch(); @@ -1774,7 +1831,8 @@ internal async Task Command_WebExecuteAsync(string queryStr // Format URI for request. Uri TargetUri = null; - ConnectedOrgPublishedEndpoints.TryGetValue(EndpointType.OrganizationDataService, out var webApiUri); + string webApiUri = null; + ConnectedOrgPublishedEndpoints?.TryGetValue(EndpointType.OrganizationDataService, out webApiUri); if (webApiUri == null || !webApiUri.Contains("/data/")) { Uri tempUri = string.IsNullOrWhiteSpace(webApiUri) ? uriOfInstance : new Uri(webApiUri); @@ -1869,12 +1927,39 @@ internal async Task Command_WebExecuteAsync(string queryStr if (ForceServerCacheConsistency && !customHeaders.ContainsKey(Utilities.RequestHeaders.FORCE_CONSISTENCY)) customHeaders.Add(Utilities.RequestHeaders.FORCE_CONSISTENCY, new List() { "Strong" }); + // handle added headers added via the RequestAdditionaHeadersAsync method. + if (RequestAdditionalHeadersAsync != null) + { + try + { + var addedHeaders = RequestAdditionalHeadersAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + if (addedHeaders != null && addedHeaders.Count() > 0) + { + foreach (var hrdProp in addedHeaders) + { + // General handling from here on out. + if (!customHeaders.ContainsKey(hrdProp.Key)) + { + customHeaders[hrdProp.Key] = new List() { hrdProp.Value }; + } + } + addedHeaders.Clear(); + } + } + finally { } // do nothing on header failure for now. + } + + if (EnableCookieRelay && CurrentCookieCollection != null) + { + customHeaders.Add("Cookie", Utilities.GetCookiesFromCollectionAsArray(CurrentCookieCollection)); + } + HttpResponseMessage resp = null; do { // Add authorization header. - Here to catch the situation where a token expires during retry. if (!customHeaders.ContainsKey(Utilities.RequestHeaders.AUTHORIZATION_HEADER)) - customHeaders.Add(Utilities.RequestHeaders.AUTHORIZATION_HEADER, new List() { string.Format("Bearer {0}", await RefreshWebProxyClientTokenAsync().ConfigureAwait(false)) }); + customHeaders.Add(Utilities.RequestHeaders.AUTHORIZATION_HEADER, new List() { string.Format("Bearer {0}", await RefreshClientTokenAsync().ConfigureAwait(false)) }); logDt.Restart(); // start clock. @@ -1895,7 +1980,6 @@ internal async Task Command_WebExecuteAsync(string queryStr contentType: contentType, requestTrackingId: requestTrackingId, sessionTrackingId: SessionTrackingId.HasValue ? SessionTrackingId.Value : Guid.Empty, - suppressDebugMessage: true, providedHttpClient: WebApiHttpClient == null ? ClientServiceProviders.Instance.GetService().CreateClient("DataverseHttpClientFactory") : WebApiHttpClient ).ConfigureAwait(false); @@ -1939,6 +2023,12 @@ internal async Task Command_WebExecuteAsync(string queryStr logDt.Stop(); } } while (retry); + + if (resp != null) + { + CurrentCookieCollection = Utilities.GetAllCookiesFromHeader(resp.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value?.ToArray(), CurrentCookieCollection); + } + return resp; } @@ -2027,9 +2117,8 @@ private bool ShouldRetryWebAPI(Exception ex, int retryCount, int maxRetryCount, /// content type to use when calling into the remote host /// Session Tracking ID to assoicate with the request. /// - /// /// - internal static async Task ExecuteHttpRequestAsync(string uri, HttpMethod method, string body = default(string), Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken), DataverseTraceLogger logSink = null, Guid? requestTrackingId = null, string contentType = default(string), Guid? sessionTrackingId = null, bool suppressDebugMessage = false, HttpClient providedHttpClient = null) + internal static async Task ExecuteHttpRequestAsync(string uri, HttpMethod method, string body = default(string), Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken), DataverseTraceLogger logSink = null, Guid? requestTrackingId = null, string contentType = default(string), Guid? sessionTrackingId = null, HttpClient providedHttpClient = null) { bool isLogEntryCreatedLocaly = false; if (logSink == null) @@ -2079,11 +2168,12 @@ private bool ShouldRetryWebAPI(Exception ex, int retryCount, int maxRetryCount, if (!_httpRequest.Headers.Contains(Utilities.RequestHeaders.X_MS_CLIENT_REQUEST_ID)) _httpRequest.Headers.TryAddWithoutValidation(Utilities.RequestHeaders.X_MS_CLIENT_REQUEST_ID, RequestId.ToString()); - if (!_httpRequest.Headers.Contains(Utilities.RequestHeaders.X_MS_CLIENT_SESSION_ID) && sessionTrackingId.HasValue) + if (!_httpRequest.Headers.Contains(Utilities.RequestHeaders.X_MS_CLIENT_SESSION_ID) && sessionTrackingId.HasValue && sessionTrackingId != Guid.Empty) _httpRequest.Headers.TryAddWithoutValidation(Utilities.RequestHeaders.X_MS_CLIENT_SESSION_ID, sessionTrackingId.ToString()); if (!_httpRequest.Headers.Contains("Connection")) _httpRequest.Headers.TryAddWithoutValidation("Connection", "Keep-Alive"); + } string _requestContent = null; @@ -2106,8 +2196,7 @@ private bool ShouldRetryWebAPI(Exception ex, int retryCount, int maxRetryCount, cancellationToken.ThrowIfCancellationRequested(); - if (!suppressDebugMessage) - logSink.Log(string.Format("Begin Sending request to {3} {0} : {2}RequestID={1}", _httpRequest.RequestUri.AbsolutePath, RequestId, sessionTrackingId.HasValue && sessionTrackingId.Value != Guid.Empty ? $" SessionID={sessionTrackingId.Value.ToString()} : " : "", method), TraceEventType.Verbose); + logSink.Log(string.Format("Begin Sending request to {3} {0} : {2}RequestID={1}", _httpRequest.RequestUri.AbsolutePath, RequestId, sessionTrackingId.HasValue && sessionTrackingId.Value != Guid.Empty ? $" SessionID={sessionTrackingId.Value.ToString()} : " : "", method), TraceEventType.Verbose); if (providedHttpClient != null) { @@ -2142,8 +2231,7 @@ private bool ShouldRetryWebAPI(Exception ex, int retryCount, int maxRetryCount, } } HttpStatusCode _statusCode = _httpResponse.StatusCode; - if (!suppressDebugMessage) - logSink.Log(string.Format("Response for request to WebAPI {5} {0} : StatusCode={1} : {4}RequestID={2} : Duration={3}", _httpRequest.RequestUri.AbsolutePath, _statusCode, RequestId, logDt.Elapsed.ToString(), sessionTrackingId.HasValue && sessionTrackingId.Value != Guid.Empty ? $" SessionID={sessionTrackingId.Value.ToString()} : " : "", method)); + logSink.Log(string.Format("Response for request to WebAPI {5} {0} : StatusCode={1} : {4}RequestID={2} : Duration={3}", _httpRequest.RequestUri.AbsolutePath, _statusCode, RequestId, logDt.Elapsed.ToString(), sessionTrackingId.HasValue && sessionTrackingId.Value != Guid.Empty ? $" SessionID={sessionTrackingId.Value.ToString()} : " : "", method), TraceEventType.Verbose); cancellationToken.ThrowIfCancellationRequested(); string _responseContent = null; @@ -2160,9 +2248,7 @@ private bool ShouldRetryWebAPI(Exception ex, int retryCount, int maxRetryCount, } ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); - if (!suppressDebugMessage) - logSink.Log(string.Format("Failure Response for request to WebAPI {5} {0} : StatusCode={1} : {4}RequestID={3} : {2}", _httpRequest.RequestUri.AbsolutePath, _statusCode, _responseContent, RequestId, sessionTrackingId.HasValue && sessionTrackingId.Value != Guid.Empty ? $" SessionID={sessionTrackingId.Value.ToString()} : " : "", method), TraceEventType.Error); - + logSink.Log(string.Format("Failure Response for request to WebAPI {5} {0} : StatusCode={1} : {4}RequestID={3} : {2}", _httpRequest.RequestUri.AbsolutePath, _statusCode, _responseContent, RequestId, sessionTrackingId.HasValue && sessionTrackingId.Value != Guid.Empty ? $" SessionID={sessionTrackingId.Value.ToString()} : " : "", method), TraceEventType.Error); _httpRequest.Dispose(); if (_httpResponse != null) @@ -2187,205 +2273,6 @@ private bool ShouldRetryWebAPI(Exception ex, int retryCount, int maxRetryCount, #endregion #region Service utilities. - - // /// - // /// Find authority and resources - // /// - // /// Service Uri endpoint - // /// Resource to connect to - // /// Discovery Service Proxy - // /// Organisation Web Proxy - // /// - // private static string FindAuthorityAndResource(Uri discoveryServiceUri, out string resource, out DiscoveryWebProxyClient svcDiscoveryProxy, out OrganizationWebProxyClient svcWebClientProxy) - //{ - // resource = discoveryServiceUri.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped); - - // UriBuilder versionTaggedUriBuilder = GetUriBuilderWithVersion(discoveryServiceUri); - - // //discoveryServiceProxy - // svcDiscoveryProxy = new DiscoveryWebProxyClient(versionTaggedUriBuilder.Uri); - // svcWebClientProxy = new OrganizationWebProxyClient(versionTaggedUriBuilder.Uri, true); - - // AuthenticationParameters ap = GetAuthorityFromTargetService(versionTaggedUriBuilder.Uri); - // if (ap != null) - // return ap.Authority; - // else - // return null; - //} - - /* - /// - /// Forming version tagged UriBuilder - /// - /// - /// - private static UriBuilder GetUriBuilderWithVersion(Uri discoveryServiceUri) - { - UriBuilder webUrlBuilder = new UriBuilder(discoveryServiceUri); - string webPath = "web"; - - if (!discoveryServiceUri.AbsolutePath.EndsWith(webPath)) - { - if (discoveryServiceUri.AbsolutePath.EndsWith("/")) - webUrlBuilder.Path = string.Concat(webUrlBuilder.Path, webPath); - else - webUrlBuilder.Path = string.Concat(webUrlBuilder.Path, "/", webPath); - } - - UriBuilder versionTaggedUriBuilder = new UriBuilder(webUrlBuilder.Uri); - string version = FileVersionInfo.GetVersionInfo(typeof(OrganizationWebProxyClient).Assembly.Location).FileVersion; - string versionQueryStringParameter = string.Format("SDKClientVersion={0}", version); - - if (string.IsNullOrEmpty(versionTaggedUriBuilder.Query)) - { - versionTaggedUriBuilder.Query = versionQueryStringParameter; - } - else if (!versionTaggedUriBuilder.Query.Contains("SDKClientVersion=")) - { - versionTaggedUriBuilder.Query = string.Format("{0}&{1}", versionTaggedUriBuilder.Query, versionQueryStringParameter); - } - - return versionTaggedUriBuilder; - } - - - /// - /// Obtaining authentication context - /// - private static AuthenticationContext ObtainAuthenticationContext(string Authority, bool requireValidation, string tokenCachePath) - { - // Do not need to dispose this here as its added ot the authentication context, its cleaned up with the authentication context later. - CdsServiceClientTokenCache tokenCache = new CdsServiceClientTokenCache(tokenCachePath); - -#if DEBUG - // When in debug mode.. Always disable Authority validation to support NOVA builds. - requireValidation = false; -#endif - - // check in cache - AuthenticationContext authenticationContext = null; - if (requireValidation == false) - { - authenticationContext = new AuthenticationContext(Authority, requireValidation, tokenCache); - } - else - { - authenticationContext = new AuthenticationContext(Authority, tokenCache); - } - return authenticationContext; - } - -#if (NET462 || NET472 || NET48) - /// - /// Obtain access token for regular popup based authentication - /// - /// Authentication Context to be used for connection - /// Resource endpoint to connect - /// Registered client Id - /// Redirect Uri - /// Prompt behavior for connecting - /// UserIdentifier - /// Authentication result with the access token for the authenticated connection - private static AuthenticationResult ObtainAccessToken(AuthenticationContext authenticationContext, string resource, string clientId, Uri redirectUri, PromptBehavior promptBehavior, UserIdentifier user) - { - PlatformParameters platformParameters = new PlatformParameters(promptBehavior); - AuthenticationResult _authenticationResult = null; - if (user != null)//If user enter username and password in connector UX - _authenticationResult = authenticationContext.AcquireTokenAsync(resource, clientId, redirectUri, platformParameters, user).Result; - else - _authenticationResult = authenticationContext.AcquireTokenAsync(resource, clientId, redirectUri, platformParameters).Result; - return _authenticationResult; - } -#endif - -#if (NET462 || NET472 || NET48) - /// - /// Obtain access token for silent login - /// - /// Authentication Context to be used for connection - /// Resource endpoint to connect - /// Registered client Id - /// Credentials passed for creating a connection - /// Authentication result with the access token for the authenticated connection - private static AuthenticationResult ObtainAccessToken(AuthenticationContext authenticationContext, string resource, string clientId, ClientCredentials clientCredentials) - { - AuthenticationResult _authenticationResult = null; - _authenticationResult = authenticationContext.AcquireTokenAsync(resource, clientId, new UserPasswordCredential(clientCredentials.UserName.UserName, clientCredentials.UserName.Password)).Result; - return _authenticationResult; - } -#endif - - /// - /// Obtain access token for certificate based login - /// - /// Authentication Context to be used for connection - /// Resource endpoint to connect - /// Registered client Id - /// X509Certificate to use to connect - /// Authentication result with the access token for the authenticated connection - private static AuthenticationResult ObtainAccessToken(AuthenticationContext authenticationContext, string resource, string clientId, X509Certificate2 clientCert) - { - ClientAssertionCertificate cred = new ClientAssertionCertificate(clientId, clientCert); - AuthenticationResult _authenticationResult = null; - _authenticationResult = authenticationContext.AcquireTokenAsync(resource, cred).Result; - return _authenticationResult; - } - -#if (NET462 || NET472 || NET48) - /// - /// Obtain access token for ClientSecret Based Login. - /// - /// Authentication Context to be used for connection - /// Resource endpoint to connect - /// Registered client Id - /// Client Secret used to connect - /// Authentication result with the access token for the authenticated connection - private static AuthenticationResult ObtainAccessToken(AuthenticationContext authenticationContext, string resource, string clientId, SecureString clientSecret) - { - ClientCredential clientCredential = new ClientCredential(clientId, new SecureClientSecret(clientSecret)); - AuthenticationResult _authenticationResult = null; - _authenticationResult = authenticationContext.AcquireTokenAsync(resource, clientCredential).Result; - return _authenticationResult; - } -#else - /// - /// Obtain access token for ClientSecret Based Login. - /// - /// Authentication Context to be used for connection - /// Resource endpoint to connect - /// Registered client Id - /// Client Secret used to connect - /// Authentication result with the access token for the authenticated connection - private static AuthenticationResult ObtainAccessToken(AuthenticationContext authenticationContext, string resource, string clientId, string clientSecret) - { - ClientCredential clientCredential = new ClientCredential(clientId, clientSecret); - AuthenticationResult _authenticationResult = null; - _authenticationResult = authenticationContext.AcquireTokenAsync(resource, clientCredential).Result; - return _authenticationResult; - } -#endif - - /// - /// Trues to get the current users login token for the target resource. - /// - /// Authentication Context to be used for connection - /// Resource endpoint to connect - /// Registered client Id - /// Credentials passed for creating a connection, username only is honored. - /// Authentication result with the access token for the authenticated connection - private static AuthenticationResult ObtainAccessTokenCurrentUser(AuthenticationContext authenticationContext, string resource, string clientId, ClientCredentials clientCredentials) - { - AuthenticationResult _authenticationResult = null; - if (clientCredentials != null && clientCredentials.UserName != null && !string.IsNullOrEmpty(clientCredentials.UserName.UserName)) - _authenticationResult = authenticationContext.AcquireTokenAsync(resource, clientId, new UserCredential(clientCredentials.UserName.UserName)).Result; - else - _authenticationResult = authenticationContext.AcquireTokenAsync(resource, clientId, new UserCredential()).Result; - - return _authenticationResult; - } - - */ - /// /// Discovers the organizations (OAuth Specific) /// @@ -2400,8 +2287,9 @@ private static AuthenticationResult ObtainAccessTokenCurrentUser(AuthenticationC /// Use the global disco path. /// (optional) If true attempts login using current user /// Logging provider + /// (optional) path to log store /// The list of organizations discovered. - internal static async Task DiscoverOrganizationsAsync(Uri discoveryServiceUri, ClientCredentials clientCredentials, string clientId, Uri redirectUri, PromptBehavior promptBehavior, bool isOnPrem, string authority, DataverseTraceLogger logSink = null, bool useGlobalDisco = false, bool useDefaultCreds = false, ILogger externalLogger = null) + internal static async Task DiscoverOrganizationsAsync(Uri discoveryServiceUri, ClientCredentials clientCredentials, string clientId, Uri redirectUri, PromptBehavior promptBehavior, bool isOnPrem, string authority, DataverseTraceLogger logSink = null, bool useGlobalDisco = false, bool useDefaultCreds = false, string tokenCacheStorePath = null, ILogger externalLogger = null) { bool isLogEntryCreatedLocaly = false; if (logSink == null) @@ -2414,10 +2302,10 @@ internal static async Task DiscoverOrganizationsAs { logSink.Log("DiscoverOrganizations - Called using user of MFA Auth for : " + discoveryServiceUri.ToString()); if (!useGlobalDisco) - return await DiscoverOrganizations_InternalAsync(discoveryServiceUri, clientCredentials, null, clientId, redirectUri, promptBehavior, isOnPrem, authority, useDefaultCreds, logSink).ConfigureAwait(false); + return await DiscoverOrganizations_InternalAsync(discoveryServiceUri, clientCredentials, null, clientId, redirectUri, promptBehavior, isOnPrem, authority, useDefaultCreds, tokenCacheStorePath: tokenCacheStorePath, logSink: logSink).ConfigureAwait(false); else { - return await DiscoverGlobalOrganizationsAsync(discoveryServiceUri, clientCredentials, null, clientId, redirectUri, promptBehavior, isOnPrem, authority, logSink, useDefaultCreds: useDefaultCreds).ConfigureAwait(false); + return await DiscoverGlobalOrganizationsAsync(discoveryServiceUri, clientCredentials, null, clientId, redirectUri, promptBehavior, isOnPrem, authority, logSink: logSink, useDefaultCreds: useDefaultCreds, tokenCacheStorePath: tokenCacheStorePath).ConfigureAwait(false); } } @@ -2438,8 +2326,9 @@ internal static async Task DiscoverOrganizationsAs /// The authority identifying the registered tenant /// (optional) Initialized CdsTraceLogger Object /// (optional) If true, attempts login with current user. + /// (optional) path to log store /// The list of organizations discovered. - internal static async Task DiscoverOrganizationsAsync(Uri discoveryServiceUri, X509Certificate2 loginCertificate, string clientId, bool isOnPrem, string authority, DataverseTraceLogger logSink = null, bool useDefaultCreds = false) + internal static async Task DiscoverOrganizationsAsync(Uri discoveryServiceUri, X509Certificate2 loginCertificate, string clientId, bool isOnPrem, string authority, DataverseTraceLogger logSink = null, bool useDefaultCreds = false, string tokenCacheStorePath = null) { bool isLogEntryCreatedLocaly = false; if (logSink == null) @@ -2450,7 +2339,7 @@ internal static async Task DiscoverOrganizationsAs try { logSink.Log("DiscoverOrganizations - Called using Certificate Auth for : " + discoveryServiceUri.ToString()); - return await DiscoverOrganizations_InternalAsync(discoveryServiceUri, null, loginCertificate, clientId, null, PromptBehavior.Never, isOnPrem, authority, useDefaultCreds, logSink).ConfigureAwait(false); + return await DiscoverOrganizations_InternalAsync(discoveryServiceUri, null, loginCertificate, clientId, null, PromptBehavior.Never, isOnPrem, authority, useDefaultCreds, logSink: logSink, tokenCacheStorePath: tokenCacheStorePath).ConfigureAwait(false); } finally { @@ -2467,8 +2356,9 @@ internal static async Task DiscoverOrganizationsAs /// Pointer to the token provider handler /// Logging endpoint (optional) /// Logging provider + /// (optional) path to log store /// Populated OrganizationDetailCollection or Null. - internal static async Task DiscoverGlobalOrganizationsAsync(Uri discoveryServiceUri, Func> tokenProviderFunction, DataverseTraceLogger logSink = null, ILogger externalLogger = null) + internal static async Task DiscoverGlobalOrganizationsAsync(Uri discoveryServiceUri, Func> tokenProviderFunction, DataverseTraceLogger logSink = null, string tokenCacheStorePath = null, ILogger externalLogger = null) { bool isLogEntryCreatedLocaly = false; if (logSink == null) @@ -2511,8 +2401,9 @@ internal static async Task DiscoverGlobalOrganizat /// The authority identifying the registered tenant /// (optional) Initialized CdsTraceLogger Object /// (optional) If true, tries to login with current users credentials + /// (optional) token filePath, if set to empty string, the default path is used /// The list of organizations discovered. - private static async Task DiscoverOrganizations_InternalAsync(Uri discoveryServiceUri, ClientCredentials clientCredentials, X509Certificate2 loginCertificate, string clientId, Uri redirectUri, PromptBehavior promptBehavior, bool isOnPrem, string authority, bool useDefaultCreds = false, DataverseTraceLogger logSink = null) + private static async Task DiscoverOrganizations_InternalAsync(Uri discoveryServiceUri, ClientCredentials clientCredentials, X509Certificate2 loginCertificate, string clientId, Uri redirectUri, PromptBehavior promptBehavior, bool isOnPrem, string authority, bool useDefaultCreds = false, string tokenCacheStorePath = null, DataverseTraceLogger logSink = null) { bool createdLogSource = false; Stopwatch dtStartQuery = new Stopwatch(); @@ -2537,9 +2428,11 @@ private static async Task DiscoverOrganizations_In // Execute Authentication Request and return token And ServiceURI IAccount user = null; object msalAuthClientOut = null; - ExecuteAuthenticationResults authRequestResult = await AuthProcessor.ExecuteAuthenticateServiceProcessAsync(discoveryServiceUri, clientCredentials, loginCertificate, clientId, redirectUri, promptBehavior, isOnPrem, authority, null, logSink: logSink, useDefaultCreds: useDefaultCreds, addVersionInfoToUri: false).ConfigureAwait(false); + ExecuteAuthenticationResults authRequestResult = await AuthProcessor.ExecuteAuthenticateServiceProcessAsync(discoveryServiceUri, clientCredentials, loginCertificate, clientId, redirectUri, promptBehavior, isOnPrem, authority, null, logSink: logSink, useDefaultCreds: useDefaultCreds, addVersionInfoToUri: false, tokenCacheStorePath: tokenCacheStorePath).ConfigureAwait(false); AuthenticationResult authenticationResult = null; - authToken = authRequestResult.GetAuthTokenAndProperties(out authenticationResult, out targetServiceUrl, out msalAuthClientOut, out authority, out resource, out user); + authToken = authRequestResult.GetAuthTokenAndProperties(out authenticationResult, out targetServiceUrl, out msalAuthClientOut, out authority, out resource, out user, out MemoryBackedTokenCache memTokenCache); + memTokenCache.ClearCache(); + memTokenCache = null; svcDiscoveryProxy = new DiscoveryWebProxyClient(targetServiceUrl); svcDiscoveryProxy.HeaderToken = authToken; @@ -2563,7 +2456,7 @@ private static async Task DiscoverOrganizations_In logSink.Log(string.Format(CultureInfo.InvariantCulture, "DiscoverOrganizations - Discovery Server Get Orgs Call Complete - Elapsed:{0}", dtStartQuery.Elapsed.ToString())); // Return the collection. - return orgResponse.Details; + return new DiscoverOrganizationsResult(orgResponse.Details, user); } catch (System.Exception ex) { @@ -2599,8 +2492,9 @@ private static async Task DiscoverOrganizations_In /// (optional) Initialized CdsTraceLogger Object /// (optional) utilize Global discovery service /// (optional) if true, attempts login with the current users credentials + /// (optional) token filePath, if set to empty string, the default path is used /// The list of organizations discovered. - private static async Task DiscoverGlobalOrganizationsAsync(Uri discoveryServiceUri, ClientCredentials clientCredentials, X509Certificate2 loginCertificate, string clientId, Uri redirectUri, PromptBehavior promptBehavior, bool isOnPrem, string authority, DataverseTraceLogger logSink = null, bool useGlobalDisco = false, bool useDefaultCreds = false) + private static async Task DiscoverGlobalOrganizationsAsync(Uri discoveryServiceUri, ClientCredentials clientCredentials, X509Certificate2 loginCertificate, string clientId, Uri redirectUri, PromptBehavior promptBehavior, bool isOnPrem, string authority, DataverseTraceLogger logSink = null, bool useGlobalDisco = false, bool useDefaultCreds = false, string tokenCacheStorePath = null) { bool createdLogSource = false; try @@ -2638,16 +2532,13 @@ private static async Task DiscoverGlobalOrganizati // Execute Authentication Request and return token And ServiceURI //Uri targetResourceRequest = new Uri(string.Format("{0}://{1}/api/discovery/", discoveryServiceUri.Scheme , discoveryServiceUri.DnsSafeHost)); - IAccount user = null; - object msalAuthClientOut = null; - AuthenticationResult authenticationResult = null; - ExecuteAuthenticationResults authRequestResult = await AuthProcessor.ExecuteAuthenticateServiceProcessAsync(authChallengeUri, clientCredentials, loginCertificate, clientId, redirectUri, promptBehavior, isOnPrem, authority, null, logSink: logSink, useDefaultCreds: useDefaultCreds, addVersionInfoToUri: false).ConfigureAwait(false); - authToken = authRequestResult.GetAuthTokenAndProperties(out authenticationResult, out targetServiceUrl, out msalAuthClientOut, out authority, out resource, out user); - + ExecuteAuthenticationResults authRequestResult = await AuthProcessor.ExecuteAuthenticateServiceProcessAsync(authChallengeUri, clientCredentials, loginCertificate, clientId, redirectUri, promptBehavior, isOnPrem, authority, null, logSink: logSink, useDefaultCreds: useDefaultCreds, addVersionInfoToUri: false, tokenCacheStorePath: tokenCacheStorePath).ConfigureAwait(false); + authToken = authRequestResult.GetAuthTokenAndProperties(out AuthenticationResult authenticationResult, out targetServiceUrl, out object msalAuthClientOut, out authority, out resource, out IAccount user, out MemoryBackedTokenCache _); // Get the GD Info and return. - return await QueryGlobalDiscoveryAsync(authToken, discoveryServiceUri, logSink).ConfigureAwait(false); + var organizationDetailCollection = await QueryGlobalDiscoveryAsync(authToken, discoveryServiceUri, logSink).ConfigureAwait(false); + return new DiscoverOrganizationsResult(organizationDetailCollection, user); } finally { @@ -2689,8 +2580,8 @@ private static async Task QueryGlobalDiscoveryAsyn try { var headers = new Dictionary>(); - headers.Add("Authorization", new List()); - headers["Authorization"].Add(string.Format("Bearer {0}", authToken)); + headers.Add(Utilities.RequestHeaders.AUTHORIZATION_HEADER, new List()); + headers[Utilities.RequestHeaders.AUTHORIZATION_HEADER].Add(string.Format("Bearer {0}", authToken)); var a = await ExecuteHttpRequestAsync(discoveryServiceUri.ToString(), HttpMethod.Get, customHeaders: headers, logSink: logSink).ConfigureAwait(false); string body = await a.Content.ReadAsStringAsync().ConfigureAwait(false); @@ -2845,31 +2736,30 @@ private async Task ConnectAndInitServiceAsync(Organization { //_ActualOrgDetailUsed = orgdata; _ActualDataverseOrgUri = BuildOrgConnectUri(orgdata); - logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Organization Service URI is = {0}", _ActualDataverseOrgUri.ToString()), TraceEventType.Information); + logEntry.Log($"Organization Service URI is '{_ActualDataverseOrgUri}'", TraceEventType.Information); // Set the Org into system config _organization = orgdata.UniqueName; ConnectedOrgFriendlyName = orgdata.FriendlyName; ConnectedOrgPublishedEndpoints = orgdata.Endpoints; - Stopwatch logDt = new Stopwatch(); + var logDt = new Stopwatch(); logDt.Start(); // Build User Credential logEntry.Log("ConnectAndInitService - Initializing Organization Service Object", TraceEventType.Verbose); // this to provide trouble shooting information when determining org connect failures. - logEntry.Log(string.Format(CultureInfo.InvariantCulture, "ConnectAndInitService - Requesting connection to Organization with Dataverse Version: {0}", orgdata.OrganizationVersion == null ? "No organization data available" : orgdata.OrganizationVersion), TraceEventType.Information); + logEntry.Log($"ConnectAndInitService - Requesting connection to Organization with Dataverse Version: {(orgdata.OrganizationVersion == null ? "No organization data available" : orgdata.OrganizationVersion)}", TraceEventType.Information); // try to create a version number from the org. OrganizationVersion = null; try { - Version tempVer = null; - if (Version.TryParse(orgdata.OrganizationVersion, out tempVer)) + if (Version.TryParse(orgdata.OrganizationVersion, out var tempVer)) OrganizationVersion = tempVer; } catch { }; - OrganizationWebProxyClient svcWebClientProxy = null; + OrganizationWebProxyClientAsync svcWebClientProxy = null; if (_eAuthType == AuthenticationType.OAuth || _eAuthType == AuthenticationType.Certificate || _eAuthType == AuthenticationType.ExternalTokenManagement @@ -2906,11 +2796,11 @@ private async Task ConnectAndInitServiceAsync(Organization else { // Execute Authentication Request and return token And ServiceURI - ExecuteAuthenticationResults authRequestResult = await AuthProcessor.ExecuteAuthenticateServiceProcessAsync(_ActualDataverseOrgUri, _UserClientCred, _certificateOfConnection, _clientId, _redirectUri, _promptBehavior, IsOnPrem, _authority, _MsalAuthClient, logEntry, useDefaultCreds: _isDefaultCredsLoginForOAuth, clientSecret: _eAuthType == AuthenticationType.ClientSecret ? _LivePass : null).ConfigureAwait(false); - authToken = authRequestResult.GetAuthTokenAndProperties(out _authenticationResultContainer, out targetServiceUrl, out _MsalAuthClient, out _authority, out _resource, out _userAccount); + ExecuteAuthenticationResults authRequestResult = await AuthProcessor.ExecuteAuthenticateServiceProcessAsync(_ActualDataverseOrgUri, _UserClientCred, _certificateOfConnection, _clientId, _redirectUri, _promptBehavior, IsOnPrem, _authority, _MsalAuthClient, logEntry, useDefaultCreds: _isDefaultCredsLoginForOAuth, clientSecret: _eAuthType == AuthenticationType.ClientSecret ? _LivePass : null, tokenCacheStorePath: _tokenCachePath).ConfigureAwait(false); + authToken = authRequestResult.GetAuthTokenAndProperties(out _authenticationResultContainer, out targetServiceUrl, out _MsalAuthClient, out _authority, out _resource, out _userAccount, out _memoryBackedTokenCache); } _ActualDataverseOrgUri = targetServiceUrl; - svcWebClientProxy = new OrganizationWebProxyClient(targetServiceUrl, true); + svcWebClientProxy = new OrganizationWebProxyClientAsync(targetServiceUrl, true); AttachWebProxyHander(svcWebClientProxy); svcWebClientProxy.HeaderToken = authToken; @@ -2933,14 +2823,14 @@ private async Task ConnectAndInitServiceAsync(Organization /// This method us used to wire up the telemetry behaviors to the webProxy connection /// /// Connection proxy to attach telemetry too - internal void AttachWebProxyHander(OrganizationWebProxyClient proxy) + internal void AttachWebProxyHander(OrganizationWebProxyClientAsync proxy) { proxy.ChannelFactory.Opening += WebProxyChannelFactory_Opening; } /// - /// Grab the Channel factory Open event and add the CrmHook Service behaviors. + /// Grab the Channel factory Open event and add the Telemetry behaviors. /// /// incoming ChannelFactory /// ignored @@ -2987,14 +2877,14 @@ byte[] encodedDataAsBytes /// - This is done, potentially replacing the original string, to deal with the discovery service returning an unusable string, for example, a DNS name that does not resolve. /// /// Org Data found from the Discovery Service. - /// CRM Connection URI + /// Connection URI private Uri BuildOrgConnectUri(OrganizationDetail orgdata) { logEntry.Log("BuildOrgConnectUri CoreClass ()", TraceEventType.Start); // Build connection URL - string CrmUrl = string.Empty; + string DvUrl = string.Empty; Uri OrgEndPoint = new Uri(orgdata.Endpoints[EndpointType.OrganizationService]); logEntry.Log("DiscoveryServer indicated organization service location = " + OrgEndPoint.ToString(), TraceEventType.Verbose); @@ -3006,7 +2896,7 @@ private Uri BuildOrgConnectUri(OrganizationDetail orgdata) #endif if (Utilities.IsValidOnlineHost(OrgEndPoint)) { - // CRM Online ..> USE PROVIDED URI. + // Online ..> USE PROVIDED URI. logEntry.Log("BuildOrgConnectUri CoreClass ()", TraceEventType.Stop); return OrgEndPoint; } @@ -3032,17 +2922,17 @@ private Uri BuildOrgConnectUri(OrganizationDetail orgdata) if (!string.IsNullOrWhiteSpace(_port)) { - CrmUrl = String.Format(CultureInfo.InvariantCulture, + DvUrl = String.Format(CultureInfo.InvariantCulture, "{0}://{1}:{2}{3}", _InternetProtocalToUse, _hostname, _port, OrgEndPoint.PathAndQuery); } else { - CrmUrl = String.Format(CultureInfo.InvariantCulture, + DvUrl = String.Format(CultureInfo.InvariantCulture, "{0}://{1}{2}", _InternetProtocalToUse, _hostname, OrgEndPoint.PathAndQuery); } logEntry.Log("BuildOrgConnectUri CoreClass ()", TraceEventType.Stop); - return new Uri(CrmUrl); + return new Uri(DvUrl); } } @@ -3056,7 +2946,7 @@ private Uri BuildOrgConnectUri(OrganizationDetail orgdata) private async Task FindDiscoveryServerAsync(DiscoveryServers onlineServerList) { OrgList orgsList = new OrgList(); - OrganizationDetailCollection col = null; + DiscoverOrganizationsResult discoverResult = null; if (_OrgDetail == null) { @@ -3073,12 +2963,12 @@ private async Task FindDiscoveryServerAsync(DiscoveryServers onlineServ if (svr.RegionalGlobalDiscoveryServer == null) { logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Trying Discovery Server, ({1}) URI is = {0}", svr.DiscoveryServerUri.ToString(), svr.DisplayName), TraceEventType.Information); - col = await QueryLiveDiscoveryServerAsync(svr.DiscoveryServerUri).ConfigureAwait(false); // Defaults to not using GD. + discoverResult = await QueryLiveDiscoveryServerAsync(svr.DiscoveryServerUri).ConfigureAwait(false); // Defaults to not using GD. } else { logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Trying Regional Global Discovery Server, ({1}) URI is = {0}", svr.RegionalGlobalDiscoveryServer.ToString(), svr.DisplayName), TraceEventType.Information); - await QueryOnlineServersListAsync(onlineServerList.OSDPServers, col, orgsList, svr.DiscoveryServerUri, svr.RegionalGlobalDiscoveryServer).ConfigureAwait(false); + await QueryOnlineServersListAsync(onlineServerList.OSDPServers, orgsList, svr.DiscoveryServerUri, svr.RegionalGlobalDiscoveryServer).ConfigureAwait(false); //col = QueryLiveDiscoveryServer(svr.DiscoveryServer); // Defaults to not using GD. return orgsList; } @@ -3089,14 +2979,14 @@ private async Task FindDiscoveryServerAsync(DiscoveryServers onlineServ { // OAuth, and GD is allowed. logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Trying Global Discovery Server ({0}) and filtering to region {1}", GlobalDiscoveryAllInstancesUri, _DataverseOnlineRegion), TraceEventType.Information); - await QueryOnlineServersListAsync(onlineServerList.OSDPServers, col, orgsList, svr.DiscoveryServerUri).ConfigureAwait(false); + await QueryOnlineServersListAsync(onlineServerList.OSDPServers, orgsList, svr.DiscoveryServerUri).ConfigureAwait(false); return orgsList; } else { - col = await QueryLiveDiscoveryServerAsync(svr.DiscoveryServerUri).ConfigureAwait(false); - if (col != null) - AddOrgToOrgList(col, svr.DisplayName, svr.DiscoveryServerUri, ref orgsList); + discoverResult = await QueryLiveDiscoveryServerAsync(svr.DiscoveryServerUri).ConfigureAwait(false); + if (discoverResult.OrganizationDetailCollection != null) + AddOrgToOrgList(discoverResult.OrganizationDetailCollection, svr.DisplayName, svr.DiscoveryServerUri, ref orgsList); } } return orgsList; @@ -3109,11 +2999,11 @@ private async Task FindDiscoveryServerAsync(DiscoveryServers onlineServ if (_eAuthType == AuthenticationType.OAuth) { // use GD. - col = await QueryLiveDiscoveryServerAsync(new Uri(GlobalDiscoveryAllInstancesUri), true).ConfigureAwait(false); - if (col != null) + discoverResult = await QueryLiveDiscoveryServerAsync(new Uri(GlobalDiscoveryAllInstancesUri), true).ConfigureAwait(false); + if (discoverResult.OrganizationDetailCollection != null) { bool isOnPrem = false; - foreach (var itm in col) + foreach (var itm in discoverResult.OrganizationDetailCollection) { var orgObj = Utilities.DeterminDiscoveryDataFromOrgDetail(new Uri(itm.Endpoints[EndpointType.OrganizationService]), out isOnPrem); AddOrgToOrgList(itm, orgObj.DisplayName, ref orgsList); @@ -3122,13 +3012,13 @@ private async Task FindDiscoveryServerAsync(DiscoveryServers onlineServ return orgsList; } else - await QueryOnlineServersListAsync(onlineServerList.OSDPServers, col, orgsList).ConfigureAwait(false); + await QueryOnlineServersListAsync(onlineServerList.OSDPServers, orgsList).ConfigureAwait(false); } else { // the org was preexisting logEntry.Log("User Specified Org details are used.", TraceEventType.Information); - col = new OrganizationDetailCollection(); + var col = new OrganizationDetailCollection(); col.Add(_OrgDetail); AddOrgToOrgList(col, "User Defined Org Detail", new Uri(_OrgDetail.Endpoints[EndpointType.OrganizationService]), ref orgsList); } @@ -3140,12 +3030,13 @@ private async Task FindDiscoveryServerAsync(DiscoveryServers onlineServ /// Iterate over the discovery servers available. /// /// - /// /// /// Forces the results to be trimmed to this region when present /// Overriding Global Discovery URI - private async Task QueryOnlineServersListAsync(ObservableCollection svrs, OrganizationDetailCollection col, OrgList orgsList, Uri trimToDiscoveryUri = null, Uri globalDiscoUriToUse = null) + private async Task QueryOnlineServersListAsync(ObservableCollection svrs, OrgList orgsList, Uri trimToDiscoveryUri = null, Uri globalDiscoUriToUse = null) { + DiscoverOrganizationsResult discoverResult; + // CHANGE HERE FOR GLOBAL DISCO ---- // Execute Global Discovery if (_eAuthType == AuthenticationType.OAuth) @@ -3154,27 +3045,25 @@ private async Task QueryOnlineServersListAsync(ObservableCollection QueryOnlineServersListAsync(ObservableCollection QueryOnlineServersListAsync(ObservableCollection /// when try, uses global discovery /// - private async Task QueryLiveDiscoveryServerAsync(Uri discoServer, bool useGlobal = false) + private async Task QueryLiveDiscoveryServerAsync(Uri discoServer, bool useGlobal = false) { logEntry.Log("QueryLiveDiscoveryServer()", TraceEventType.Start); try { if (_eAuthType == AuthenticationType.OAuth || _eAuthType == AuthenticationType.ClientSecret) { - return await DiscoverOrganizationsAsync(discoServer, _UserClientCred, _clientId, _redirectUri, _promptBehavior, false, _authority, logEntry, useGlobalDisco: useGlobal).ConfigureAwait(false); + return await DiscoverOrganizationsAsync(discoServer, _UserClientCred, _clientId, _redirectUri, _promptBehavior, false, _authority, logSink: logEntry, useGlobalDisco: useGlobal, tokenCacheStorePath: _tokenCachePath).ConfigureAwait(false); } else { if (_eAuthType == AuthenticationType.Certificate) { - return await DiscoverOrganizationsAsync(discoServer, _certificateOfConnection, _clientId, false, _authority, logEntry).ConfigureAwait(false); + return await DiscoverOrganizationsAsync(discoServer, _certificateOfConnection, _clientId, false, _authority, logEntry, tokenCacheStorePath: _tokenCachePath).ConfigureAwait(false); } return null; @@ -3287,31 +3174,35 @@ private void AddOrgToOrgList(OrganizationDetail organizationDetail, string disco /// /// Refresh web proxy client token /// - internal async Task RefreshWebProxyClientTokenAsync() + internal async Task RefreshClientTokenAsync() { string clientToken = string.Empty; if (_authenticationResultContainer != null && !string.IsNullOrEmpty(_resource) && !string.IsNullOrEmpty(_clientId)) { - if (_MsalAuthClient is IPublicClientApplication pClient) + if (_authenticationResultContainer.ExpiresOn.ToUniversalTime() < DateTime.UtcNow.AddMinutes(1)) { - // this is a user based application. - if (_isCalledbyExecuteRequest && _promptBehavior != PromptBehavior.Never) +#if DEBUG + System.Diagnostics.Trace.WriteLine($">>>>>>>>>>>>>>>>>>>> REQUESTING TOKEN {_ServiceCACHEName} - {Thread.CurrentThread.ManagedThreadId} - IsAClone {IsAClone} >>>>>>>>>>>>>>>>>>>>"); +#endif + + if (_MsalAuthClient is IPublicClientApplication pClient) { - _isCalledbyExecuteRequest = false; + // this is a user based application. + if (_isCalledbyExecuteRequest && _promptBehavior != PromptBehavior.Never) + { + _isCalledbyExecuteRequest = false; + } _authenticationResultContainer = await AuthProcessor.ObtainAccessTokenAsync(pClient, _authenticationResultContainer.Scopes.ToList(), _authenticationResultContainer.Account, _promptBehavior, _UserClientCred, _isDefaultCredsLoginForOAuth, logEntry).ConfigureAwait(false); } else { - _authenticationResultContainer = await AuthProcessor.ObtainAccessTokenAsync(pClient, _authenticationResultContainer.Scopes.ToList(), _authenticationResultContainer.Account, _promptBehavior, _UserClientCred, _isDefaultCredsLoginForOAuth, logEntry).ConfigureAwait(false); - } - } - else - { - if (_MsalAuthClient is IConfidentialClientApplication cClient) - { - _authenticationResultContainer = await AuthProcessor.ObtainAccessTokenAsync(cClient, _authenticationResultContainer.Scopes.ToList(), logEntry).ConfigureAwait(false); + if (_MsalAuthClient is IConfidentialClientApplication cClient) + { + _authenticationResultContainer = await AuthProcessor.ObtainAccessTokenAsync(cClient, _authenticationResultContainer.Scopes.ToList(), logEntry).ConfigureAwait(false); + } } } + clientToken = _authenticationResultContainer.AccessToken; if (_svcWebClientProxy != null) _svcWebClientProxy.HeaderToken = clientToken; @@ -3329,12 +3220,12 @@ internal async Task RefreshWebProxyClientTokenAsync() _svcWebClientProxy.HeaderToken = clientToken; } else - throw new Exception("External Authentication Requested but not configured correctly. Faulted In Request Access Token 004"); + throw new DataverseOperationException("External Authentication Requested but not configured correctly. Faulted In Request Access Token 004"); } catch (Exception ex) { - throw new Exception("External Authentication Requested but not configured correctly. 005", ex); + throw new DataverseOperationException("External Authentication Requested but not configured correctly. 005", ex); } } @@ -3376,7 +3267,7 @@ void Dispose(bool disposing) if (unqueInstance) { // Clean the connect out of memory. - System.Runtime.Caching.MemoryCache.Default.Remove(_ServiceCACHEName); + _clientMemoryCache.Remove(_ServiceCACHEName); } try @@ -3386,8 +3277,15 @@ void Dispose(bool disposing) if (_svcWebClientProxy.Endpoint.EndpointBehaviors.Contains(typeof(DataverseTelemetryBehaviors))) { _svcWebClientProxy.ChannelFactory.Opening -= WebProxyChannelFactory_Opening; - _svcWebClientProxy.ChannelFactory.Endpoint.EndpointBehaviors.Remove(typeof(DataverseTelemetryBehaviors)); - _svcWebClientProxy.Endpoint.EndpointBehaviors.Remove(typeof(DataverseTelemetryBehaviors)); + if (_svcWebClientProxy.ChannelFactory.Endpoint.EndpointBehaviors.Contains(typeof(DataverseTelemetryBehaviors))) + { + _svcWebClientProxy.ChannelFactory.Endpoint.EndpointBehaviors.Remove(typeof(DataverseTelemetryBehaviors)); + } + + if (_svcWebClientProxy.Endpoint.EndpointBehaviors.Contains(typeof(DataverseTelemetryBehaviors))) + { + _svcWebClientProxy.Endpoint.EndpointBehaviors.Remove(typeof(DataverseTelemetryBehaviors)); + } } } } diff --git a/src/GeneralTools/DataverseClient/Client/DataverseDataTypeWrapper.cs b/src/GeneralTools/DataverseClient/Client/DataverseDataTypeWrapper.cs index 7c7ac93..6ebe7b4 100644 --- a/src/GeneralTools/DataverseClient/Client/DataverseDataTypeWrapper.cs +++ b/src/GeneralTools/DataverseClient/Client/DataverseDataTypeWrapper.cs @@ -20,11 +20,11 @@ public enum DataverseFieldType /// DateTime, /// - /// Decimal, for money, use CrmMoney - Converts from a decimal type + /// Decimal, for money, use Money - Converts from a decimal type /// Decimal, /// - /// Double type, while CRM calls this a float, it is actually a double for money, use CrmMoney - Converts from a double type + /// Double type, while Dataverse calls this a float, it is actually a double for money, use Money - Converts from a double type /// Float, /// @@ -32,11 +32,11 @@ public enum DataverseFieldType /// Money, /// - /// CRM whole number - Converts from a Int type + /// Whole number - Converts from a Int type /// Number, /// - /// Ref Type for CDS, Creates an EntityReference + /// Ref Type for Dataverse, Creates an EntityReference /// You need to provide a Guid as a value, and a the name of an entity for the lookup key /// Customer, @@ -45,7 +45,7 @@ public enum DataverseFieldType /// Key, /// - /// Ref Type for CDS, Creates an EntityReference + /// Ref Type for Dataverse, Creates an EntityReference /// You need to provide a Guid as a value, and a the name of an entity for the lookup key /// Lookup, @@ -63,7 +63,7 @@ public enum DataverseFieldType /// UniqueIdentifier, /// - /// User Specified type... will be appended directly. This type must be one of the valid CRM 2011 types + /// User Specified type... will be appended directly. This type must be one of the valid Dataverse types /// Raw @@ -91,14 +91,14 @@ public class DataverseDataTypeWrapper public string ReferencedEntity { get; set; } /// - /// Create a new CRM Data Type + /// Create a new Data Type /// Default Constructor /// public DataverseDataTypeWrapper() { } /// - /// Create a new CDS Data Type + /// Create a new Data Type /// /// Data to Set /// Type of Data to Set @@ -110,7 +110,7 @@ public DataverseDataTypeWrapper(object data, DataverseFieldType CdsFieldType) } /// - /// Create a new CDS Data Type + /// Create a new Data Type /// /// Data to Set /// Type of Data to Set diff --git a/src/GeneralTools/DataverseClient/Client/DataverseTelemetryBehaviors.cs b/src/GeneralTools/DataverseClient/Client/DataverseTelemetryBehaviors.cs index c93e31d..6c28595 100644 --- a/src/GeneralTools/DataverseClient/Client/DataverseTelemetryBehaviors.cs +++ b/src/GeneralTools/DataverseClient/Client/DataverseTelemetryBehaviors.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Configuration; using System.Linq; using System.Net; @@ -110,14 +111,6 @@ public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRu clientRuntime.ClientMessageInspectors.Add(this); #endif - // when Set, this will ask WCF to transit cookies. - if (_callerCdsConnectionServiceHandler.EnableCookieRelay) - { - if (endpoint.Binding is BasicHttpBinding) - { - ((BasicHttpBinding)endpoint.Binding).AllowCookies = true; - } - } // Override the Max Fault size if required. if (_maxFaultSize != -1) { @@ -133,9 +126,9 @@ public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRu // Override the max received size if required. if (_maxReceivedMessageSize != -1) { - if (endpoint.Binding is BasicHttpBinding) + if (endpoint.Binding is BasicHttpBinding binding) { - ((BasicHttpBinding)endpoint.Binding).MaxReceivedMessageSize = _maxReceivedMessageSize; + binding.MaxReceivedMessageSize = _maxReceivedMessageSize; } } } @@ -152,6 +145,22 @@ public void Validate(ServiceEndpoint endpoint) [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")] public void AfterReceiveReply(ref Message reply, object correlationState) { + try + { + HttpResponseMessageProperty httpResponseMessage = null; + if (reply.Properties.TryGetValue(HttpResponseMessageProperty.Name, out object httpRequestMessageObject)) + { + httpResponseMessage = httpRequestMessageObject as HttpResponseMessageProperty; + } + + if (httpResponseMessage != null && httpResponseMessage.Headers.Count > 0) + { + var a = httpResponseMessage.Headers["Set-Cookie"]; + if (a != null) + _callerCdsConnectionServiceHandler.CurrentCookieCollection = Utilities.GetAllCookiesFromHeader(httpResponseMessage.Headers["Set-Cookie"] , _callerCdsConnectionServiceHandler.CurrentCookieCollection); + } + } + finally { }; } /// @@ -188,22 +197,22 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) } // Adding HTTP Headers + HttpRequestMessageProperty httpRequestMessage = null; object httpRequestMessageObject; if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject)) { - HttpRequestMessageProperty httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty; - if (string.IsNullOrEmpty(httpRequestMessage.Headers[Utilities.RequestHeaders.USER_AGENT_HTTP_HEADER])) - { - httpRequestMessage.Headers[Utilities.RequestHeaders.USER_AGENT_HTTP_HEADER] = _userAgent; - } - if (string.IsNullOrEmpty(httpRequestMessage.Headers[HttpRequestHeader.Connection])) - { - httpRequestMessage.Headers.Add(HttpRequestHeader.Connection, Utilities.RequestHeaders.CONNECTION_KEEP_ALIVE); - } - if (OrganizationRequestId != Guid.Empty && string.IsNullOrEmpty(httpRequestMessage.Headers[Utilities.RequestHeaders.X_MS_CLIENT_REQUEST_ID])) - { - httpRequestMessage.Headers[Utilities.RequestHeaders.X_MS_CLIENT_REQUEST_ID] = OrganizationRequestId.ToString(); - } + httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty; + } + else + { + httpRequestMessage = new HttpRequestMessageProperty(); + } + + if (httpRequestMessage != null) + { + httpRequestMessage.Headers[Utilities.RequestHeaders.USER_AGENT_HTTP_HEADER] = _userAgent; + httpRequestMessage.Headers.Add(HttpRequestHeader.Connection, Utilities.RequestHeaders.CONNECTION_KEEP_ALIVE); + httpRequestMessage.Headers[Utilities.RequestHeaders.X_MS_CLIENT_REQUEST_ID] = OrganizationRequestId.ToString(); if ((_callerCdsConnectionServiceHandler != null && (_callerCdsConnectionServiceHandler.SessionTrackingId.HasValue && _callerCdsConnectionServiceHandler.SessionTrackingId.Value != Guid.Empty)) && string.IsNullOrEmpty(httpRequestMessage.Headers[Utilities.RequestHeaders.X_MS_CLIENT_SESSION_ID])) { httpRequestMessage.Headers[Utilities.RequestHeaders.X_MS_CLIENT_SESSION_ID] = _callerCdsConnectionServiceHandler.SessionTrackingId.Value.ToString(); @@ -212,27 +221,16 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) { httpRequestMessage.Headers[Utilities.RequestHeaders.FORCE_CONSISTENCY] = "Strong"; } - } - else - { - HttpRequestMessageProperty httpRequestMessage = new HttpRequestMessageProperty(); - httpRequestMessage.Headers.Add(Utilities.RequestHeaders.USER_AGENT_HTTP_HEADER, _userAgent); - httpRequestMessage.Headers.Add(HttpRequestHeader.Connection, Utilities.RequestHeaders.CONNECTION_KEEP_ALIVE); - if (OrganizationRequestId != Guid.Empty) - { - httpRequestMessage.Headers.Add(Utilities.RequestHeaders.X_MS_CLIENT_REQUEST_ID, OrganizationRequestId.ToString()); - } - if (_callerCdsConnectionServiceHandler != null && (_callerCdsConnectionServiceHandler.SessionTrackingId.HasValue && _callerCdsConnectionServiceHandler.SessionTrackingId.Value != Guid.Empty)) + if (_callerCdsConnectionServiceHandler.EnableCookieRelay) { - httpRequestMessage.Headers.Add(Utilities.RequestHeaders.X_MS_CLIENT_SESSION_ID, _callerCdsConnectionServiceHandler.SessionTrackingId.Value.ToString()); - } - if ((_callerCdsConnectionServiceHandler != null && _callerCdsConnectionServiceHandler.ForceServerCacheConsistency && string.IsNullOrEmpty(httpRequestMessage.Headers[Utilities.RequestHeaders.FORCE_CONSISTENCY]))) - { - httpRequestMessage.Headers.Add(Utilities.RequestHeaders.FORCE_CONSISTENCY, "Strong"); + if (_callerCdsConnectionServiceHandler.CurrentCookieCollection != null) + httpRequestMessage.Headers["Cookie"] = Utilities.GetCookiesFromCollectionAsString(_callerCdsConnectionServiceHandler.CurrentCookieCollection); } - request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage); + AddExternalHeaders(httpRequestMessage); + if (httpRequestMessageObject == null) + request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage); } // Adding SOAP headers @@ -243,11 +241,11 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) callerId = _callerCdsConnectionServiceHandler.WebClient.CallerId; } - if (callerId == Guid.Empty) // Prefer the CRM Caller ID over hte AADObjectID. + if (callerId == Guid.Empty) // Prefer the Caller ID over the AADObjectID. { if (_callerCdsConnectionServiceHandler != null && (_callerCdsConnectionServiceHandler.CallerAADObjectId.HasValue && _callerCdsConnectionServiceHandler.CallerAADObjectId.Value != Guid.Empty)) { - // Add Caller ID to the SOAP Envolope. + // Add Caller ID to the SOAP Envelope. // Set a header request with the AAD Caller Object ID. using (OperationContextScope scope = new OperationContextScope((IContextChannel)channel)) { @@ -285,5 +283,33 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel) } return null; } + + private void AddExternalHeaders(HttpRequestMessageProperty httpRequestMessage) + { + if (_callerCdsConnectionServiceHandler.RequestAdditionalHeadersAsync != null) + { + try + { + var addedHeaders = _callerCdsConnectionServiceHandler.RequestAdditionalHeadersAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + if (addedHeaders != null && addedHeaders.Count() > 0) + { + foreach (var hrdProp in addedHeaders) + { + // General handling from here on out. + if (httpRequestMessage.Headers.AllKeys.Contains(hrdProp.Key)) + { + httpRequestMessage.Headers.Add(hrdProp.Key, hrdProp.Value); + } + else + { + httpRequestMessage.Headers[hrdProp.Key] = hrdProp.Value; + } + } + addedHeaders.Clear(); + } + } + finally { } // do nothing on header failure for now. + } + } } } diff --git a/src/GeneralTools/DataverseClient/Client/DataverseTraceLogger.cs b/src/GeneralTools/DataverseClient/Client/DataverseTraceLogger.cs index 12d64ea..086ae5e 100644 --- a/src/GeneralTools/DataverseClient/Client/DataverseTraceLogger.cs +++ b/src/GeneralTools/DataverseClient/Client/DataverseTraceLogger.cs @@ -28,7 +28,7 @@ internal sealed class DataverseTraceLogger : TraceLoggerBase #region Properties /// - /// Last Error from CRM + /// Last Error from Dataverse /// public new string LastError { diff --git a/src/GeneralTools/DataverseClient/Client/DynamicEntityUtility.cs b/src/GeneralTools/DataverseClient/Client/DynamicEntityUtility.cs index 0c22fed..3642764 100644 --- a/src/GeneralTools/DataverseClient/Client/DynamicEntityUtility.cs +++ b/src/GeneralTools/DataverseClient/Client/DynamicEntityUtility.cs @@ -32,7 +32,7 @@ internal List GetAttributeDataByEntity(string entityName, params } /// - /// Retrieve metadata for a single CRM record + /// Retrieve metadata for a single Dataverse record /// /// A string name of the entity type being retrieved e.g. contact /// A Guid representing the record id we want to retrieve diff --git a/src/GeneralTools/DataverseClient/Client/FxCopExclusions.cs b/src/GeneralTools/DataverseClient/Client/FxCopExclusions.cs index 1148702..a6248ae 100644 --- a/src/GeneralTools/DataverseClient/Client/FxCopExclusions.cs +++ b/src/GeneralTools/DataverseClient/Client/FxCopExclusions.cs @@ -9,34 +9,15 @@ [module: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportFileItem+DataDelimiterCode")] [module: SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportFileItem+DataDelimiterCode")] [module: SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.BooleanAttributeData.#BooleanOptions")] -[module: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+CrmSearchFilter")] -[module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService+ManagedTokenOrganizationServiceProxy.#.ctor(Microsoft.Xrm.Sdk.Client.IServiceManagement`1,System.ServiceModel.Description.ClientCredentials)")] -[module: SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+CrmSearchFilter.#SearchConditions")] -[module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+CrmSearchFilter.#SearchConditions")] [module: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportFileItem+FieldDelimiterCode")] [module: SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportFileItem+FieldDelimiterCode")] [module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.MetadataUtility.#GetRequiredAttributesByEntity(System.String)")] -[module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#get_CrmHostName()")] -[module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#set_CrmHostName(System.String)")] -[module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#get_InternetProtocalToUse()")] -[module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#set_CustomerOrganization(System.String)")] -[module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#set_CrmHostPort(System.String)")] -[module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#get_CrmHostPort()")] -[module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#get_CrmServiceAccessCredential()")] -[module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#set_CrmServiceAccessCredential(System.Net.NetworkCredential)")] -[module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.Model.CdsDiscoveryServers.#set_OSDPServers(System.Collections.ObjectModel.ObservableCollection`1)")] -[module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.Model.CdsDiscoveryServers.#set_OSDPServers(System.Collections.ObjectModel.ObservableCollection`1)")] -[module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.Model.CdsDiscoveryServers.#set_Servers(System.Collections.ObjectModel.ObservableCollection`1)")] - -[module: SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsFieldType.#UniqueIdentifier", MessageId = "UniqueIdentifier")] [module: SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.BatchManager.#AddNewRequestToBatch(System.Guid,Microsoft.Xrm.Sdk.OrganizationRequest,System.String)")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.BatchManager.#AddNewRequestToBatch(System.Guid,Microsoft.Xrm.Sdk.OrganizationRequest,System.String)")] [module: SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.BatchManager.#CreateNewBatch(System.String,System.Boolean,System.Boolean)")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.BatchManager.#GetRequestBatchByName(System.String)")] [module: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportRequest+ImportMode")] -[module: SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.AuthenticationType.#IFD", MessageId = "IFD")] -[module: SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.LiveDevice.#Token", MessageId = "System.Xml.XmlNode", Justification = "This is required for proper XML Serialization")] [module: SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+PickListMetaElement", MessageId = "PickList")] [module: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+PickListMetaElement")] [module: SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+PickListMetaElement.#PickListLabel", MessageId = "PickList")] @@ -44,29 +25,6 @@ [module: SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+PickListMetaElement.#Items")] [module: SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+PickListMetaElement.#.ctor(System.String,System.String,System.String)", MessageId = "pickList")] [module: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+FormTypeId")] -[module: SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsTraceLogger.#GetExceptionDetail(System.Object,System.Text.StringBuilder,System.Int32,System.Text.StringBuilder)")] -[module: SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsTraceLogger.#GetExceptionDetail(System.Object,System.Text.StringBuilder,System.Int32,System.Text.StringBuilder)")] -[module: SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsTraceLogger.#GetExceptionDetail(System.Object,System.Text.StringBuilder,System.Int32,System.Text.StringBuilder)", MessageId = "System.Int32.ToString")] -[module: SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsTraceLogger.#Log(System.String,System.Diagnostics.TraceEventType,System.Exception)", MessageId = "message")] -[module: SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsTraceLogger.#FormatExceptionMessage(System.String,System.String,System.String,System.String,System.Text.StringBuilder,System.Int32)", MessageId = "source")] -[module: SuppressMessage("Microsoft.Security", "CA9881:ClassesShouldBeSealed", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsServiceTelemetryBehaviors", MessageId = "Microsoft.PowerPlatform.Dataverse.Client.CdsServiceTelemetryBehaviors")] -[module: SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsServiceTelemetryBehaviors")] -[module: SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#InitCRM2011Service()")] -[module: SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#InitCRM2011Service()")] -[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#InitCRM2011Service()")] -[module: SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#InitCRM2011Service()")] -[module: SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#CreateAndAuthenticateProxy`1(Microsoft.Xrm.Sdk.Client.IServiceManagement`1,System.Uri,System.Uri,System.ServiceModel.Description.ClientCredentials,System.ServiceModel.Description.ClientCredentials,System.String)")] -[module: SuppressMessage("Microsoft.Usage", "CA9888:DisposeObjectsCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#CreateAndAuthenticateProxy`1(Microsoft.Xrm.Sdk.Client.IServiceManagement`1,System.Uri,System.Uri,System.ServiceModel.Description.ClientCredentials,System.ServiceModel.Description.ClientCredentials,System.String)", MessageId = "OutObject")] -[module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#get_InternetProtocalToUse()")] -[module: SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#DiscoverOrganizations(System.Uri,System.Uri,System.ServiceModel.Description.ClientCredentials,System.ServiceModel.Description.ClientCredentials)")] -[module: SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#DiscoverOrganizations(System.Uri,System.Uri,System.ServiceModel.Description.ClientCredentials,System.ServiceModel.Description.ClientCredentials)")] -[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#DiscoverOrganizations(System.Uri,System.Uri,System.ServiceModel.Description.ClientCredentials,System.ServiceModel.Description.ClientCredentials)")] -[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#DoLiveIdLogin(System.Boolean)")] -[module: SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#ConnectAndInitCrmOrgService(Microsoft.Xrm.Sdk.Discovery.OrganizationDetail,System.Boolean,System.Uri)")] -[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#ConnectAndInitCrmOrgService(Microsoft.Xrm.Sdk.Discovery.OrganizationDetail,System.Boolean,System.Uri)")] -[module: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+CrmFilterConditionItem")] -[module: SuppressMessage("Microsoft.Security", "CA9881:ClassesShouldBeSealed", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsDataTypeWrapper", MessageId = "Microsoft.PowerPlatform.Dataverse.Client.CdsDataTypeWrapper")] -[module: SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsDataTypeWrapper.#Type")] [module: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportFileItem+FileTypeCode")] [module: SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportFileItem+FileTypeCode.#CSV", MessageId = "CSV")] [module: SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportFileItem+FileTypeCode.#XML", MessageId = "XML")] @@ -74,154 +32,49 @@ [module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityFormIdListByType(System.String,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+FormTypeId)", MessageId = "Logicalname")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityFormIdListByType(System.String,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+FormTypeId)")] [module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityFormIdListByType(System.String,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+FormTypeId)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#ImportDataMapToCrm(System.String,System.Boolean,System.Boolean)")] -[module: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#ImportSolutionToCrm(System.String,System.Guid&,System.Boolean,System.Boolean,System.Boolean)", MessageId = "1#")] -[module: SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#ImportSolutionToCrm(System.String,System.Guid&,System.Boolean,System.Boolean,System.Boolean)", MessageId = "Un")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#ImportSolutionToCrm(System.String,System.Guid&,System.Boolean,System.Boolean,System.Boolean)")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#ImportSolutionToCrm(System.String,System.Guid&,System.Boolean,System.Boolean,System.Boolean)", MessageId = "Dependancy")] -[module: SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#ImportSolutionToCrm(System.String,System.Guid&,System.Boolean,System.Boolean,System.Boolean)", MessageId = "UnManaged")] -[module: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByFetchSearch(System.String,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] -[module: SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByFetchSearch(System.String,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)", MessageId = "batchId")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByFetchSearch(System.String,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByFetchSearch(System.String,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)", MessageId = "4#")] -[module: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByFetchSearch(System.String,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)", MessageId = "5#")] [module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDisplayName(System.String,System.Int32)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CancelSalesOrder(System.Guid,System.Collections.Generic.Dictionary`2,System.Int32,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)", MessageId = "9#")] -[module: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)", MessageId = "8#")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#UpdateStateAndStatusForEntity(System.String,System.Guid,System.String,System.String,System.Guid)")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#UpdateStateAndStatusForEntity(System.String,System.Guid,System.String,System.String,System.Guid)", MessageId = "ent")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataById(System.String,System.Guid,System.Collections.Generic.List`1,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataById(System.String,System.Guid,System.Collections.Generic.List`1,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataById(System.String,System.Guid,System.Collections.Generic.List`1,System.Guid)")] [module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateBatchOperationRequest(System.String,System.Boolean,System.Boolean)")] -[module: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetProductsByLinkedSearchToAccount(System.Guid,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetProductsByLinkedSearchToAccount(System.Guid,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)", MessageId = "rollupfrom")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)", MessageId = "11#")] -[module: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)", MessageId = "10#")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#IsSampleDataInstalled()")] -[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CrmCommand_Delete(System.String,System.Guid,System.String)")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetGlobalOptionSetMetadata(System.String)")] [module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityMetadata(System.String,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityMetadata(System.String,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] [module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityMetadata(System.String,Microsoft.Xrm.Sdk.Metadata.EntityFilters)", MessageId = "Logicalname")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#ExecuteWorkflowOnEntity(System.String,System.Guid,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#AssignEntityToUser(System.Guid,System.String,System.Guid,System.Guid)")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetDataByKeyFromResultsSet`1(System.Collections.Generic.Dictionary`2,System.String)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#UpdateStateAndStatusForEntity(System.String,System.Guid,System.Int32,System.Int32,System.Guid)")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#UpdateStateAndStatusForEntity(System.String,System.Guid,System.Int32,System.Int32,System.Guid)", MessageId = "statuscode")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#UpdateStateAndStatusForEntity(System.String,System.Guid,System.Int32,System.Int32,System.Guid)", MessageId = "statecode")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#UpdateStateAndStatusForEntity(System.String,System.Guid,System.Int32,System.Int32,System.Guid)", MessageId = "ent")] [module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetAllAttributesForEntity(System.String)", MessageId = "Logicalname")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetAllAttributesForEntity(System.String)")] [module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetAllAttributesForEntity(System.String)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#.ctor(System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.Boolean,System.Boolean,Microsoft.Xrm.Sdk.Discovery.OrganizationDetail)")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityAttributeMetadataForAttribute(System.String,System.String)")] [module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityAttributeMetadataForAttribute(System.String,System.String)", MessageId = "Logicalname")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#DeleteEntity(System.String,System.Guid,System.Guid)")] [module: SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateOrUpdatePickListElement(System.String,System.String,System.Collections.Generic.List`1,System.Int32,System.Boolean)", MessageId = "PickList")] [module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateOrUpdatePickListElement(System.String,System.String,System.Collections.Generic.List`1,System.Int32,System.Boolean)")] [module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetAllEntityMetadata(System.Boolean,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetAllEntityMetadata(System.Boolean,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] [module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetAllEntityMetadata(System.Boolean,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByFetchSearchEC(System.String,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByFetchSearchEC(System.String,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)", MessageId = "4#")] -[module: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByFetchSearchEC(System.String,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)", MessageId = "5#")] -[module: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)", MessageId = "9#")] -[module: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)", MessageId = "10#")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateNewActivityEntry(System.String,System.String,System.Guid,System.String,System.String,System.String,System.Collections.Generic.Dictionary`2,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateNewActivityEntry(System.String,System.String,System.Guid,System.String,System.String,System.String,System.Collections.Generic.Dictionary`2,System.Guid)")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#AddDataToResultSet(System.Collections.Generic.Dictionary`2&,Microsoft.Xrm.Sdk.Entity)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateAnnotation(System.String,System.Guid,System.Collections.Generic.Dictionary`2,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetMyCrmUserId()")] -[module: SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateNewOrder(System.Guid,System.String,System.String,System.String,System.Guid)", MessageId = "priceList")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateNewOrder(System.Guid,System.String,System.String,System.String,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.Dictionary`2,System.String,System.Collections.Generic.Dictionary`2,System.String,System.String,System.String,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid)")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.Dictionary`2,System.String,System.Collections.Generic.Dictionary`2,System.String,System.String,System.String,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid)", MessageId = "m")] -[module: SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.Dictionary`2,System.String,System.Collections.Generic.Dictionary`2,System.String,System.String,System.String,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid)", MessageId = "batchId")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.Dictionary`2,System.String,System.Collections.Generic.Dictionary`2,System.String,System.String,System.String,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.Dictionary`2,System.String,System.Collections.Generic.Dictionary`2,System.String,System.String,System.String,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#ExecuteCrmOrganizationRequest(Microsoft.Xrm.Sdk.OrganizationRequest,System.String)")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#ExecuteCrmOrganizationRequest(Microsoft.Xrm.Sdk.OrganizationRequest,System.String)", MessageId = "req")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CloseTroubleTicket(System.Guid,System.String,System.String,System.Guid)")] [module: SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetPickListElementFromMetadataEntity(System.String,System.String)", MessageId = "PickList")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#UpdateEntity(System.String,System.String,System.Guid,System.Collections.Generic.Dictionary`2,System.String,System.Boolean,System.Guid)")] [module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#ResetLocalMetadataCache(System.String)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByFetchSearchEC(System.String,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CrmCommand_Execute(Microsoft.Xrm.Sdk.OrganizationRequest,System.String)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#.ctor(System.String,System.String,System.String,System.String,System.Boolean,System.Boolean,Microsoft.Xrm.Sdk.Discovery.OrganizationDetail,System.Boolean)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CloseOpportunity(System.Guid,System.Collections.Generic.Dictionary`2,System.Int32,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#ExecuteCrmEntityDeleteRequest(System.String,System.Guid,System.String)")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#ExecuteCrmEntityDeleteRequest(System.String,System.Guid,System.String)", MessageId = "ent")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#.ctor(System.Net.NetworkCredential,System.String,System.String,System.String,System.Boolean,System.Boolean,Microsoft.Xrm.Sdk.Discovery.OrganizationDetail)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#.ctor(System.Net.NetworkCredential,Microsoft.PowerPlatform.Dataverse.Client.AuthenticationType,System.String,System.String,System.String,System.Boolean,System.Boolean,Microsoft.Xrm.Sdk.Discovery.OrganizationDetail)")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDisplayNameImpl(System.String,System.Int32,System.Boolean)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#AddEntityToQueue(System.Guid,System.String,System.String,System.Guid,System.Boolean,System.Guid)")] [module: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] [module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] [module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)")] [module: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)", MessageId = "9#")] [module: SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&,System.Guid)", MessageId = "10#")] -[module: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateFaxActivity(System.String,System.String,System.String,System.Guid,System.String)")] [module: SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#SubmitImportRequest(Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportRequest,System.DateTime)")] [module: SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#SubmitImportRequest(Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportRequest,System.DateTime)")] [module: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Guid)")] [module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Guid)", MessageId = "rollupfrom")] [module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Guid)")] [module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Guid)")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateMultiEntityAssociation(System.String,System.Guid,System.String,System.Collections.Generic.List`1,System.String,System.Guid,System.Boolean)", MessageId = "Multi")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateMultiEntityAssociation(System.String,System.Guid,System.String,System.Collections.Generic.List`1,System.String,System.Guid,System.Boolean)", MessageId = "Entitie")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateMultiEntityAssociation(System.String,System.Guid,System.String,System.Collections.Generic.List`1,System.String,System.Guid,System.Boolean)")] -[module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateMultiEntityAssociation(System.String,System.Guid,System.String,System.Collections.Generic.List`1,System.String,System.Guid,System.Boolean)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#DeleteEntityAssociation(System.String,System.Guid,System.String,System.Guid,System.String,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateEntityAssociation(System.String,System.Guid,System.String,System.Guid,System.String,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CreateNewRecord(System.String,System.Collections.Generic.Dictionary`2,System.String,System.Boolean,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.Dictionary`2,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.Dictionary`2,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.Dictionary`2,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CloseQuote(System.Guid,System.Collections.Generic.Dictionary`2,System.Int32,System.Guid)")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityTypeCode(System.String)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#AttachProductInstanceToOrder(System.Guid,System.Guid,System.String,System.String,System.Decimal,System.Decimal,System.Guid)")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#AttachProductInstanceToOrder(System.Guid,System.Guid,System.String,System.String,System.Decimal,System.Decimal,System.Guid)", MessageId = "quanity")] [module: SuppressMessage("Microsoft.Usage", "CA9888:DisposeObjectsCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#MakeSecureString(System.String)", MessageId = "_pass")] -[module: SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid,System.Boolean)", MessageId = "System.String.Format(System.String,System.Object)")] -[module: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid,System.Boolean)")] -[module: SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid,System.Boolean)", MessageId = "searchOperator")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid,System.Boolean)")] -[module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid,System.Boolean)")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSearchOperator,System.Collections.Generic.List`1,System.Guid,System.Boolean)", MessageId = "m")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CloseActivity(System.String,System.Guid,System.String,System.String,System.Guid)")] [module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDisplayNamePlural(System.String,System.Int32)")] -[module: SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByFetchSearch(System.String,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#GetEntityDataByFetchSearch(System.String,System.Guid)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#CloseIncident(System.Guid,System.Collections.Generic.Dictionary`2,System.Int32,System.Guid)")] -[module: SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#AddValueToPropertyList(System.Collections.Generic.KeyValuePair`2,Microsoft.Xrm.Sdk.AttributeCollection)")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#LookupEntitiyID(System.String,System.String,System.String,System.String)")] -[module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#SendSingleEmail(System.Guid,System.String,System.Guid)")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#SendSingleEmail(System.Guid,System.String,System.Guid)", MessageId = "emailid")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "assembly", Target = "Microsoft.PowerPlatform.Cds.Client", MessageId = "Xrm")] -[module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "namespace", Target = "Microsoft.PowerPlatform.Cds.Client", MessageId = "Xrm")] [module: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Scope = "namespace", Target = "Microsoft.PowerPlatform.Dataverse.Client.Model", MessageId = "Xrm")] [module: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportRequest")] [module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportRequest.#Files")] [module: SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportRequest.#Files")] -[module: SuppressMessage("Microsoft.Security", "CA9881:ClassesShouldBeSealed", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.crmMessageInspector", MessageId = "Microsoft.PowerPlatform.Dataverse.Client.crmMessageInspector")] -[module: SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.crmMessageInspector", MessageId = "crm")] [module: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.RequestBatch.#.ctor(System.String,System.Boolean,System.Boolean)")] [module: SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.RequestBatch.#BatchItems")] [module: SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.RequestBatch.#BatchItems")] @@ -229,8 +82,4 @@ [module: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+LogicalSortOrder")] [module: SuppressMessage("Microsoft.Security", "CA9881:ClassesShouldBeSealed", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportFileItem", MessageId = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportFileItem")] [module: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient+ImportFileItem")] -[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.DeviceIdManager.#ReadExistingDevice(Microsoft.PowerPlatform.Dataverse.Client.DeviceIdManager+EnvironmentConfiguration)")] -[module: SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#CreateAndAuthenticateProxy`1(Microsoft.Xrm.Sdk.Client.IServiceManagement`1,System.Uri,System.Uri,System.ServiceModel.Description.ClientCredentials,System.ServiceModel.Description.ClientCredentials,System.String)")] -[module: SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.CdsConnectionService.#ConnectAndInitCrmOrgService(Microsoft.Xrm.Sdk.Discovery.OrganizationDetail,System.Boolean,System.Uri)")] -[module: SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.#MakeSecureString(System.String)")] [module: SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.PowerPlatform.Dataverse.Client.ServiceClient.#MakeSecureString(System.String)")] diff --git a/src/GeneralTools/DataverseClient/Client/GlobalSuppressions.cs b/src/GeneralTools/DataverseClient/Client/GlobalSuppressions.cs index 3d8c9fc..c68b24c 100644 --- a/src/GeneralTools/DataverseClient/Client/GlobalSuppressions.cs +++ b/src/GeneralTools/DataverseClient/Client/GlobalSuppressions.cs @@ -1,349 +1,9 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. // -// To add a suppression to this file, right-click the message in the -// Error List, point to "Suppress Message(s)", and click -// "In Project Suppression File". -// You do not need to add suppressions to this file manually. -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "DynamicsCrm.Connector.Model.CdsDiscoveryServers.#OSDPServers")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "DynamicsCrm.Connector.Model.CdsDiscoveryServers.#Servers")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.Model.CdsDiscoveryServers.#GetServerShortNameByDisplayName(System.String,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "DynamicsCrm.Connector.Model.CdsDiscoveryServers.#NotifyPropertyChanged(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#InitCRM2011Service()")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#InitCRM2011Service()")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#InitCRM2011Service()")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#CustomerOrganization")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#CrmServiceAccessCredential")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#CrmHostPort")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#CrmHostName")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.Int32.Parse(System.String)", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#ConnectAndInitCrmOrgService(Microsoft.Xrm.Sdk.Discovery.OrganizationDetail,System.Boolean,System.Uri)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#CreateAndAuthenticateProxy`1(Microsoft.Xrm.Sdk.Client.IServiceManagement`1,System.Uri,System.Uri,System.ServiceModel.Description.ClientCredentials,System.ServiceModel.Description.ClientCredentials,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#CreateAndAuthenticateProxy`1(Microsoft.Xrm.Sdk.Client.IServiceManagement`1,System.Uri,System.Uri,System.ServiceModel.Description.ClientCredentials,System.ServiceModel.Description.ClientCredentials,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#ConnectAndInitCrmOrgService(Microsoft.Xrm.Sdk.Discovery.OrganizationDetail,System.Boolean,System.Uri)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#ConnectAndInitCrmOrgService(Microsoft.Xrm.Sdk.Discovery.OrganizationDetail,System.Boolean,System.Uri)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#ConnectAndInitCrmOrgService(Microsoft.Xrm.Sdk.Discovery.OrganizationDetail,System.Boolean,System.Uri)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "discoveryServerUri", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#AddOrgToOrgList(Microsoft.Xrm.Sdk.Discovery.OrganizationDetail,System.String,System.Uri,DynamicsCrm.Connector.Model.CdsOrgList&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#AddOrgToOrgList(Microsoft.Xrm.Sdk.Discovery.OrganizationDetail,System.String,System.Uri,DynamicsCrm.Connector.Model.CdsOrgList&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "domain", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#.ctor(DynamicsCrm.Connector.AuthenticationType,System.String,System.String,System.String,System.String,System.String,System.Security.SecureString,System.String,System.Boolean,Microsoft.Xrm.Sdk.Discovery.OrganizationDetail)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DynamicsCrm.Connector.Model")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#DiscoverOrganizations(System.Uri,System.Uri,System.ServiceModel.Description.ClientCredentials,System.ServiceModel.Description.ClientCredentials)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#DiscoverOrganizations(System.Uri,System.Uri,System.ServiceModel.Description.ClientCredentials,System.ServiceModel.Description.ClientCredentials)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#InitCRM2011Service()")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "DynamicsCrm.Connector.#CrmCommand_Delete(System.String,System.Guid,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.#CrmCommand_Delete(System.String,System.Guid,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.#CrmCommand_Execute(Microsoft.Xrm.Sdk.OrganizationRequest,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "9#", Scope = "member", Target = "DynamicsCrm.Connector.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "10#", Scope = "member", Target = "DynamicsCrm.Connector.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "10#", Scope = "member", Target = "DynamicsCrm.Connector.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "9#", Scope = "member", Target = "DynamicsCrm.Connector.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.#GetAllAttributesForEntity(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetAllAttributesForEntity(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Logicalname", Scope = "member", Target = "DynamicsCrm.Connector.#GetAllAttributesForEntity(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#GetAllEntityMetadata(System.Boolean,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.#GetAllEntityMetadata(System.Boolean,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetAllEntityMetadata(System.Boolean,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityAttributeMetadataForAttribute(System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Logicalname", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityAttributeMetadataForAttribute(System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByFetchSearch(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "4#", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByFetchSearch(System.String,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "5#", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByFetchSearch(System.String,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByFetchSearch(System.String,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "m", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "rollupfrom", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "10#", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "11#", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "rollupfrom", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "8#", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "9#", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDisplayName(System.String,System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDisplayNameImpl(System.String,System.Int32,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDisplayNamePlural(System.String,System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityFormIdListByType(System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+FormTypeIds)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityFormIdListByType(System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+FormTypeIds)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Logicalname", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityFormIdListByType(System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+FormTypeIds)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityMetadata(System.String,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityMetadata(System.String,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Logicalname", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityMetadata(System.String,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityTypeCode(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.#GetGlobalOptionSetMetadata(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#ImportDataMapToCrm(System.String,System.Boolean,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Scope = "member", Target = "DynamicsCrm.Connector.#ImportSolutionToCrm(System.String,System.Guid&,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#ImportSolutionToCrm(System.String,System.Guid&,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Plugins", Scope = "member", Target = "DynamicsCrm.Connector.#ImportSolutionToCrm(System.String,System.Guid&,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.#IsSampleDataInstalled()")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.#LookupEntitiyID(System.String,System.String,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "DynamicsCrm.Connector.#MakeSecureString(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "ent", Scope = "member", Target = "DynamicsCrm.Connector.#UpdateStateAndStatusForEntity(System.String,System.Guid,System.Int32,System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "ent", Scope = "member", Target = "DynamicsCrm.Connector.#UpdateStateAndStatusForEntity(System.String,System.Guid,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "string", Scope = "member", Target = "DynamicsCrm.Connector.#UpdateStateAndStatusForEntity(System.String,System.Guid,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CrmSearchFilter.#SearchConditions")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#CreateMultiEntityAssociation(System.String,System.Guid,System.String,System.Collections.Generic.List`1,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.ImportRequest.#Files")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.#CreateOrUpdatePickListElement(System.String,System.String,System.Collections.Generic.List`1,System.Int32,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Scope = "type", Target = "DynamicsCrm.Connector.ImportFileItem+DataDelimiterCode")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Scope = "type", Target = "DynamicsCrm.Connector.ImportFileItem+FieldDelimiterCode")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "10#", Scope = "member", Target = "DynamicsCrm.Connector.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "11#", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "9#", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "9#", Scope = "member", Target = "DynamicsCrm.Connector.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "10#", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "8#", Scope = "member", Target = "DynamicsCrm.Connector.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#AddEntityToQueue(System.Guid,System.String,System.String,System.Guid,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#CancelSalesOrder(System.Guid,System.Collections.Generic.Dictionary`2,System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#CloseActivity(System.String,System.Guid,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#CloseIncident(System.Guid,System.Collections.Generic.Dictionary`2,System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#CloseOpportunity(System.Guid,System.Collections.Generic.Dictionary`2,System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#CloseQuote(System.Guid,System.Collections.Generic.Dictionary`2,System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#CreateNewActivityEntry(System.String,System.String,System.Guid,System.String,System.String,System.String,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#.ctor(System.Net.NetworkCredential,DynamicsCrm.Connector.AuthenticationType,System.String,System.String,System.String,System.Boolean,System.Boolean,Microsoft.Xrm.Sdk.Discovery.OrganizationDetail)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#.ctor(System.Net.NetworkCredential,System.String,System.String,System.String,System.Boolean,System.Boolean,Microsoft.Xrm.Sdk.Discovery.OrganizationDetail)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#.ctor(System.String,System.String,System.String,System.String,System.Boolean,System.Boolean,Microsoft.Xrm.Sdk.Discovery.OrganizationDetail,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.#.ctor(System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.Boolean,System.Boolean,Microsoft.Xrm.Sdk.Discovery.OrganizationDetail)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.#AddDataToResultSet(System.Collections.Generic.Dictionary`2&,Microsoft.Xrm.Sdk.Entity)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.#CreateNewActivityEntry(System.String,System.String,System.Guid,System.String,System.String,System.String,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.DeviceIdManager.#ReadExistingDevice(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetAllAttributesForEntity(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetAllEntityMetadata(System.Boolean,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityFormIdListByType(System.String,DynamicsCrm.Connector.FormTypeIds)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CreateMultiEntityAssociation(System.String,System.Guid,System.String,System.Collections.Generic.List`1,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CreateOrUpdatePickListElement(System.String,System.String,System.Collections.Generic.List`1,System.Int32,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.PickListMetaElement.#Items")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataById(System.String,System.Guid,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.Dictionary`2,System.String,System.Collections.Generic.Dictionary`2,System.String,System.String,System.String,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.Dictionary`2,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByFetchSearch(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByFetchSearch(System.String,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.Dictionary`2,System.String,System.Collections.Generic.Dictionary`2,System.String,System.String,System.String,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.Dictionary`2,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetProductsByLinkedSearchToAccount(System.Guid)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CreateNewCustomer(System.Object,System.Guid&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#ImportSolutionToCrm(System.String,System.Guid&,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "10#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "10#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "5#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByFetchSearch(System.String,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "11#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "9#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "9#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "9#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "4#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByFetchSearch(System.String,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "10#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "8#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#AddEntityToQueue(System.Guid,System.String,System.String,System.Guid,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CancelSalesOrder(System.Guid,System.Collections.Generic.Dictionary`2,System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CloseActivity(System.String,System.Guid,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CloseIncident(System.Guid,System.Collections.Generic.Dictionary`2,System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CloseOpportunity(System.Guid,System.Collections.Generic.Dictionary`2,System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CloseQuote(System.Guid,System.Collections.Generic.Dictionary`2,System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CreateNewActivityEntry(System.String,System.String,System.Guid,System.String,System.String,System.String,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#.ctor(System.Net.NetworkCredential,DynamicsCrm.Connector.AuthenticationType,System.String,System.String,System.String,System.Boolean,System.Boolean,Microsoft.Xrm.Sdk.Discovery.OrganizationDetail)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#.ctor(System.Net.NetworkCredential,System.String,System.String,System.String,System.Boolean,System.Boolean,Microsoft.Xrm.Sdk.Discovery.OrganizationDetail)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#.ctor(System.String,System.String,System.String,System.String,System.Boolean,System.Boolean,Microsoft.Xrm.Sdk.Discovery.OrganizationDetail,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#.ctor(System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.Boolean,System.Boolean,Microsoft.Xrm.Sdk.Discovery.OrganizationDetail)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetAllEntityMetadata(System.Boolean,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDisplayName(System.String,System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDisplayNamePlural(System.String,System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityMetadata(System.String,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#ImportDataMapToCrm(System.String,System.Boolean,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#ImportSolutionToCrm(System.String,System.Guid&,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#AddDataToResultSet(System.Collections.Generic.Dictionary`2&,Microsoft.Xrm.Sdk.Entity)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CreateNewActivityEntry(System.String,System.String,System.Guid,System.String,System.String,System.String,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CrmCommand_Delete(System.String,System.Guid,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CrmCommand_Execute(Microsoft.Xrm.Sdk.OrganizationRequest,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetAllAttributesForEntity(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetAllEntityMetadata(System.Boolean,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetDataByKeyFromResultsSet`1(System.Collections.Generic.Dictionary`2,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityAttributeMetadataForAttribute(System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataById(System.String,System.Guid,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDisplayNameImpl(System.String,System.Int32,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityFormIdListByType(System.String,DynamicsCrm.Connector.FormTypeIds)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityMetadata(System.String,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityTypeCode(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetGlobalOptionSetMetadata(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#IsSampleDataInstalled()")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#LookupEntitiyID(System.String,System.String,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CreateMultiEntityAssociation(System.String,System.Guid,System.String,System.Collections.Generic.List`1,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.Dictionary`2,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Puid", Scope = "member", Target = "DynamicsCrm.Connector.DeviceRegistrationResponse.#Puid")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "entityid", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#AddEntityToQueue(System.Guid,System.String,System.String,System.Guid,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "quanity", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#AttachProductInstanceToOrder(System.Guid,System.Guid,System.String,System.String,System.Decimal,System.Decimal)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "statecode", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CloseActivity(System.String,System.Guid,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "statuscode", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CloseActivity(System.String,System.Guid,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Entitie", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CreateMultiEntityAssociation(System.String,System.Guid,System.String,System.Collections.Generic.List`1,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "entityid", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CreateNewOrder(System.Guid,System.String,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "pricelistname", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CreateNewOrder(System.Guid,System.String,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Logicalname", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetAllAttributesForEntity(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Logicalname", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityAttributeMetadataForAttribute(System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "m", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.Dictionary`2,System.String,System.Collections.Generic.Dictionary`2,System.String,System.String,System.String,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.Dictionary`2,System.String,System.Collections.Generic.Dictionary`2,System.String,System.String,System.String,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "m", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "rollupfrom", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "rollupfrom", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Logicalname", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityFormIdListByType(System.String,DynamicsCrm.Connector.FormTypeIds)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Logicalname", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityMetadata(System.String,Microsoft.Xrm.Sdk.Metadata.EntityFilters)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Plugins", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#ImportSolutionToCrm(System.String,System.Guid&,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "emailid", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#SendSingleEmail(System.Guid,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "ent", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#UpdateStateAndStatusForEntity(System.String,System.Guid,System.Int32,System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "ent", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#UpdateStateAndStatusForEntity(System.String,System.Guid,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CSV", Scope = "member", Target = "DynamicsCrm.Connector.ImportFileItem+FileTypeCode.#CSV")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "public", Scope = "member", Target = "DynamicsCrm.Connector.DeviceRegistrationErrorCode.#publicError")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XML", Scope = "member", Target = "DynamicsCrm.Connector.ImportFileItem+FileTypeCode.#XML")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "string", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#UpdateStateAndStatusForEntity(System.String,System.Guid,System.String,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CreateNewCustomer(System.Object,System.Guid&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Scope = "member", Target = "DynamicsCrm.Connector.DeviceIdManager.#.cctor()")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#InternetProtocalToUse")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService+ManagedTokenOrganizationServiceProxy.#.ctor(Microsoft.Xrm.Sdk.Client.IServiceManagement`1,System.ServiceModel.Description.ClientCredentials)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CreateFaxActivity(System.String,System.String,System.String,System.Guid,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#CrmCommand_Delete(System.String,System.Guid,System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "DynamicsCrm.Connector.DynamicEntityUtility.#RetrieveMultipleAsDynamicEntities(Microsoft.Xrm.Sdk.Query.QueryBase)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "DynamicsCrm.Connector.MetadataUtility.#GetRequiredAttributesByEntity(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#MakeSecureString(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#DiscoverOrganizations(System.Uri,System.Uri,System.ServiceModel.Description.ClientCredentials,System.ServiceModel.Description.ClientCredentials)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "DeviceIdManager", Scope = "member", Target = "DynamicsCrm.Connector.DeviceUserName.#ThrowIfNoEncryption()")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "UseEncryptionApis", Scope = "member", Target = "DynamicsCrm.Connector.DeviceUserName.#ThrowIfNoEncryption()")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#AddValueToPropertyList(System.Collections.Generic.KeyValuePair`2,Microsoft.Xrm.Sdk.AttributeCollection)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "DynamicsCrm.Connector.CrmSearchFilter.#SearchConditions")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "DynamicsCrm.Connector.ImportRequest.#Files")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "DynamicsCrm.Connector.PickListMetaElement.#Items")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.ImportFileItem+DataDelimiterCode")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.ImportFileItem+FieldDelimiterCode")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.ImportFileItem+FileTypeCode")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.ImportRequest+ImportMode")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetMyCrmUserId()")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Scope = "member", Target = "DynamicsCrm.Connector.DeviceUserName.#ThrowIfNoEncryption()")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#ResetLocalMetadataCache(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "searchOperator", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Logicalname", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityFormIdListByType(System.String,DynamicsCrm.Connector.FormTypeId)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityFormIdListByType(System.String,DynamicsCrm.Connector.FormTypeId)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#ResetLocalMetadataCache(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityFormIdListByType(System.String,DynamicsCrm.Connector.FormTypeId)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+ImportFileItem+FileTypeCode")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+ImportRequest")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+ImportRequest+ImportMode")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSortOrder")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+PickListItem")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+PickListMetaElement")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#SubmitImportRequest(DynamicsCrm.Connector.CdsConnectionServiceActions+ImportRequest,System.DateTime)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#SubmitImportRequest(DynamicsCrm.Connector.CdsConnectionServiceActions+ImportRequest,System.DateTime)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#SubmitImportRequest(DynamicsCrm.Connector.CdsConnectionServiceActions+ImportRequest,System.DateTime)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.Dictionary`2,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Picklist", Scope = "member", Target = "DynamicsCrm.Connector.CdsFieldType.#Picklist")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Picklist", Scope = "member", Target = "DynamicsCrm.Connector.PicklistAttributeData.#PicklistOptions")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Picklist", Scope = "type", Target = "DynamicsCrm.Connector.PicklistAttributeData")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "m", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.Dictionary`2,System.String,System.Collections.Generic.Dictionary`2,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.Dictionary`2,System.String,System.Collections.Generic.Dictionary`2,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "m", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "rollupfrom", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "rollupfrom", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Logicalname", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityFormIdListByType(System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+FormTypeId)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CSV", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+ImportFileItem+FileTypeCode.#CSV")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XML", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+ImportFileItem+FileTypeCode.#XML")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Scope = "member", Target = "DynamicsCrm.Connector.CdsDataTypeWrapper.#Type")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "searchOperator", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Scope = "member", Target = "DynamicsCrm.Connector.BooleanAttributeData.#BooleanOptions")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Scope = "member", Target = "DynamicsCrm.Connector.PicklistAttributeData.#PicklistOptions")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+CrmSearchFilter.#SearchConditions")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+ImportRequest.#Files")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+PickListMetaElement.#Items")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+CrmSearchFilter.#SearchConditions")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+ImportRequest.#Files")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+PickListMetaElement.#Items")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityFormIdListByType(System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+FormTypeId)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.Dictionary`2,System.String,System.Collections.Generic.Dictionary`2,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.Dictionary`2,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.Dictionary`2,System.String,System.Collections.Generic.Dictionary`2,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByLinkedSearch(System.String,System.Collections.Generic.List`1,System.String,System.Collections.Generic.List`1,System.String,System.String,System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.Dictionary`2,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+ImportFileItem+DataDelimiterCode")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+ImportFileItem+FieldDelimiterCode")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "10#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "10#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "11#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "9#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "9#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.Dictionary`2,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "9#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetActivitiesBy(System.String,System.Guid,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "10#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataByRollup(System.String,System.Guid,System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "8#", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityDataBySearchParams(System.String,System.Collections.Generic.List`1,DynamicsCrm.Connector.CdsConnectionServiceActions+LogicalSearchOperator,System.Collections.Generic.List`1,System.Collections.Generic.Dictionary`2,System.Int32,System.Int32,System.String,System.String&,System.Boolean&)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionService.#DoLiveIdLogin(System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions.#GetEntityFormIdListByType(System.String,DynamicsCrm.Connector.CdsConnectionServiceActions+FormTypeId)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+CrmFilterConditionItem")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+CrmSearchFilter")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+FormTypeId")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+ImportFileItem")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+ImportFileItem+DataDelimiterCode")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "DynamicsCrm.Connector.CdsConnectionServiceActions+ImportFileItem+FieldDelimiterCode")] +// To add a suppression to this file, right-click the message in the +// Error List, point to "Suppress Message(s)", and click +// "In Project Suppression File". +// You do not need to add suppressions to this file manually. diff --git a/src/GeneralTools/DataverseClient/Client/Interfaces/IOrganizationServiceAsync.cs b/src/GeneralTools/DataverseClient/Client/Interfaces/IOrganizationServiceAsync.cs index 657fd68..b1eea16 100644 --- a/src/GeneralTools/DataverseClient/Client/Interfaces/IOrganizationServiceAsync.cs +++ b/src/GeneralTools/DataverseClient/Client/Interfaces/IOrganizationServiceAsync.cs @@ -1,4 +1,5 @@ using System; +using System.ServiceModel; using System.Threading; using System.Threading.Tasks; using Microsoft.Xrm.Sdk; @@ -8,25 +9,20 @@ namespace Microsoft.PowerPlatform.Dataverse.Client { /// /// Interface containing extension methods provided by the DataverseServiceClient for the IOrganizationService Interface. - /// These extensions will only operate from within the client and are not supported server side. + /// These extensions will only operate from within the client and are not supported server side. /// - public interface IOrganizationServiceAsync + [ServiceContract(Name = "IOrganizationService", Namespace = Xrm.Sdk.XmlNamespaces.V5.Services)] + [KnownAssembly] + public interface IOrganizationServiceAsync: IOrganizationService { /// /// Create an entity and process any related entities /// /// entity to create - /// Propagates notification that operations should be canceled. /// The ID of the created record - Task CreateAsync(Entity entity, CancellationToken cancellationToken); + [OperationContract] - /// - /// Create an entity and process any related entities - /// - /// entity to create - /// Propagates notification that operations should be canceled. - /// Returns the newly created record - Task CreateAndReturnAsync(Entity entity, CancellationToken cancellationToken); + Task CreateAsync(Entity entity); /// /// Retrieves instance of an entity @@ -34,32 +30,36 @@ public interface IOrganizationServiceAsync /// Logical name of entity /// Id of entity /// Column Set collection to return with the request - /// Propagates notification that operations should be canceled. /// Selected Entity - Task RetrieveAsync(string entityName, Guid id, ColumnSet columnSet, CancellationToken cancellationToken); + [OperationContract] + + Task RetrieveAsync(string entityName, Guid id, ColumnSet columnSet); /// /// Updates an entity and process any related entities /// /// entity to update - /// Propagates notification that operations should be canceled. - Task UpdateAsync(Entity entity, CancellationToken cancellationToken); + [OperationContract] + + Task UpdateAsync(Entity entity); /// /// Delete instance of an entity /// /// Logical name of entity /// Id of entity - /// Propagates notification that operations should be canceled. - Task DeleteAsync(string entityName, Guid id, CancellationToken cancellationToken); + [OperationContract] + + Task DeleteAsync(string entityName, Guid id); /// /// Perform an action in an organization specified by the request. /// /// Refer to SDK documentation for list of messages that can be used. /// Results from processing the request - /// Propagates notification that operations should be canceled. - Task ExecuteAsync(OrganizationRequest request , CancellationToken cancellationToken); + [OperationContract] + + Task ExecuteAsync(OrganizationRequest request ); /// /// Associate an entity with a set of entities @@ -68,8 +68,9 @@ public interface IOrganizationServiceAsync /// /// /// - /// Propagates notification that operations should be canceled. - Task AssociateAsync(string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection relatedEntities, CancellationToken cancellationToken); + [OperationContract] + + Task AssociateAsync(string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection relatedEntities); /// /// Disassociate an entity with a set of entities @@ -78,15 +79,17 @@ public interface IOrganizationServiceAsync /// /// /// - /// Propagates notification that operations should be canceled. - Task DisassociateAsync(string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection relatedEntities, CancellationToken cancellationToken); + [OperationContract] + + Task DisassociateAsync(string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection relatedEntities); /// /// Retrieves a collection of entities /// /// - /// Propagates notification that operations should be canceled. /// Returns an EntityCollection Object containing the results of the query - Task RetrieveMultipleAsync(QueryBase query, CancellationToken cancellationToken); + [OperationContract] + + Task RetrieveMultipleAsync(QueryBase query); } } diff --git a/src/GeneralTools/DataverseClient/Client/Interfaces/IOrganizationServiceAsync2.cs b/src/GeneralTools/DataverseClient/Client/Interfaces/IOrganizationServiceAsync2.cs new file mode 100644 index 0000000..112cb61 --- /dev/null +++ b/src/GeneralTools/DataverseClient/Client/Interfaces/IOrganizationServiceAsync2.cs @@ -0,0 +1,25 @@ +using System; +using System.ServiceModel; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Xrm.Sdk; +using Microsoft.Xrm.Sdk.Query; + +namespace Microsoft.PowerPlatform.Dataverse.Client +{ + /// + /// Interface containing extension methods provided by the DataverseServiceClient for the IOrganizationService Interface. + /// These extensions will only operate from within the client and are not supported server side. + /// + public interface IOrganizationServiceAsync2 + { + /// + /// Create an entity and process any related entities + /// + /// entity to create + /// Propagates notification that operations should be canceled. + /// Returns the newly created record + Task CreateAndReturnAsync(Entity entity, CancellationToken cancellationToken); + + } +} diff --git a/src/GeneralTools/DataverseClient/Client/MetadataUtility.cs b/src/GeneralTools/DataverseClient/Client/MetadataUtility.cs index 9be8819..214cfa6 100644 --- a/src/GeneralTools/DataverseClient/Client/MetadataUtility.cs +++ b/src/GeneralTools/DataverseClient/Client/MetadataUtility.cs @@ -69,14 +69,14 @@ public void ClearCachedEntityMetadata(string entityName) } /// - /// Retrieves all metadata from the CRM solution.. this is a time consuming task + /// Retrieves all metadata from the solution.. this is a time consuming task /// /// only return "published" or "published state" of entities /// the depth if detail on the entity to retrieve /// public List GetAllEntityMetadata(bool onlyPublished, EntityFilters filter = EntityFilters.Default) { - // this will force a retrieve of all metatdata from CRM's entities + // this will force a retrieve of all metatdata from entities List results = new List(); RetrieveAllEntitiesRequest request = new RetrieveAllEntitiesRequest(); @@ -115,13 +115,13 @@ public List GetAllEntityMetadata(bool onlyPublished, EntityFilte /// /// Returns Entity Metadata for requested entity. - /// Applies returns all data available based on CRM version type + /// Applies returns all data available based on version type /// /// Name of the Entity, data is being requested on /// Entity data public EntityMetadata GetEntityMetadata(string entityName) { - // Filter the EntityFitlers based on the version of CRM being connected too. + // Filter the EntityFitlers based on the version of Dataverse being connected too. if (svcAct.ConnectedOrgVersion < Version.Parse("7.1.0.0")) return GetEntityMetadata(EntityFilters.Attributes | EntityFilters.Entity | EntityFilters.Privileges | EntityFilters.Relationships , entityName); else @@ -293,7 +293,7 @@ public List GetAllAttributesMetadataByEntity(string entityNam EntityMetadata entityMetadata = GetEntityMetadata(entityName); if (entityMetadata != null) { - // Added to deal with failed call to CRM. + // Added to deal with failed call to Dataverse. if (entityMetadata.Attributes != null) { List results = new List(); @@ -350,7 +350,7 @@ public OptionSetMetadata GetGlobalOptionSetMetadata(string optionSetName) // Create the RetrieveOption Set RetrieveOptionSetRequest optReq = new RetrieveOptionSetRequest { Name = optionSetName }; - // query CRM + // query Dataverse RetrieveOptionSetResponse response = (RetrieveOptionSetResponse)svcAct.Command_Execute(optReq, "GetGlobalOptionSetMetadata"); if (response != null) { diff --git a/src/GeneralTools/DataverseClient/Client/Microsoft.PowerPlatform.Dataverse.Client.csproj b/src/GeneralTools/DataverseClient/Client/Microsoft.PowerPlatform.Dataverse.Client.csproj index 168d91b..af8d65a 100644 --- a/src/GeneralTools/DataverseClient/Client/Microsoft.PowerPlatform.Dataverse.Client.csproj +++ b/src/GeneralTools/DataverseClient/Client/Microsoft.PowerPlatform.Dataverse.Client.csproj @@ -28,18 +28,19 @@ - - - + + + - + + diff --git a/src/GeneralTools/DataverseClient/Client/Model/AppSettingsConfiguration.cs b/src/GeneralTools/DataverseClient/Client/Model/AppSettingsConfiguration.cs index 1291d8a..3b55721 100644 --- a/src/GeneralTools/DataverseClient/Client/Model/AppSettingsConfiguration.cs +++ b/src/GeneralTools/DataverseClient/Client/Model/AppSettingsConfiguration.cs @@ -14,6 +14,7 @@ namespace Microsoft.PowerPlatform.Dataverse.Client.Model /// public class AppSettingsConfiguration { + #region Dataverse Interaction Settings private int _maxRetryCount = Utils.AppSettingsHelper.GetAppSetting("ApiOperationRetryCountOverride", 10); /// @@ -25,7 +26,8 @@ public int MaxRetryCount set => _maxRetryCount = value; } - private TimeSpan _retryPauseTime = Utils.AppSettingsHelper.GetAppSetting("ApiOperationRetryDelayOverride", new TimeSpan(0, 0, 0, 5)); + + private TimeSpan _retryPauseTime = Utils.AppSettingsHelper.GetAppSettingTimeSpan("ApiOperationRetryDelayOverride", Utils.AppSettingsHelper.TimeSpanFromKey.Seconds, new TimeSpan(0, 0, 0, 5)); /// /// Amount of time to wait between retries @@ -36,15 +38,62 @@ public TimeSpan RetryPauseTime set => _retryPauseTime = value; } - private bool _useWebApi = Utils.AppSettingsHelper.GetAppSetting("UseWebApi", false); + private bool _useWebApi = Utils.AppSettingsHelper.GetAppSetting("UseWebApi", true); /// - /// Use Web API instead of legacy SOAP org service + /// Use Web API instead of org service /// public bool UseWebApi { get => _useWebApi; set => _useWebApi = value; } + + private bool _useWebApiLoginFlow = Utils.AppSettingsHelper.GetAppSetting("UseWebApiLoginFlow", false); + /// + /// Use Web API instead of org service for logging into and getting boot up data. + /// + public bool UseWebApiLoginFlow + { + get => _useWebApiLoginFlow; + set => _useWebApiLoginFlow = value; + } + + #endregion + + #region MSAL Settings. + private TimeSpan _msalTimeout = Utils.AppSettingsHelper.GetAppSettingTimeSpan("MSALRequestTimeoutOverride", Utils.AppSettingsHelper.TimeSpanFromKey.Seconds, new TimeSpan(0, 0, 0, 30)); + + /// + /// Amount of time to wait for MSAL/AAD to wait for a token response before timing out + /// + public TimeSpan MSALRequestTimeout + { + get => _msalTimeout; + set => _msalTimeout = value; + } + + private int _msalRetryCount = Utils.AppSettingsHelper.GetAppSetting("MSALRequestRetryCountOverride", 3); + + /// + /// Number of retries to Get a token from MSAL. + /// + public int MsalRetryCount + { + get => _msalRetryCount; + set => _msalRetryCount = value; + } + + private bool _msalEnablePIIInLog = Utils.AppSettingsHelper.GetAppSetting("MSALLogPII", false); + + /// + /// Enabled Logging of PII in MSAL Log. - defaults to false. + /// + public bool MSALEnabledLogPII + { + get => _msalEnablePIIInLog; + set => _msalEnablePIIInLog = value; + } + #endregion } } diff --git a/src/GeneralTools/DataverseClient/Client/Model/DiscoverOrganizationsResult.cs b/src/GeneralTools/DataverseClient/Client/Model/DiscoverOrganizationsResult.cs new file mode 100644 index 0000000..cc77a9b --- /dev/null +++ b/src/GeneralTools/DataverseClient/Client/Model/DiscoverOrganizationsResult.cs @@ -0,0 +1,39 @@ +#region using +using Microsoft.Identity.Client; +using Microsoft.Xrm.Sdk.Discovery; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +#endregion + +namespace Microsoft.PowerPlatform.Dataverse.Client.Model +{ + /// + /// Result of call to DiscoverOrganizationsAsync + /// + public class DiscoverOrganizationsResult + { + /// + /// Constructor + /// + /// OrganizationDetailCollection + /// account + public DiscoverOrganizationsResult(OrganizationDetailCollection organizationDetailCollection, IAccount account) + { + OrganizationDetailCollection = organizationDetailCollection; + Account = account; + } + + /// + /// OrganizationDetailCollection + /// + public OrganizationDetailCollection OrganizationDetailCollection { get; private set; } + + /// + /// MSAL account selected as part of dicovery authentication + /// + public IAccount Account { get; private set; } + } +} diff --git a/src/GeneralTools/DataverseClient/Client/Model/DiscoveryServers.cs b/src/GeneralTools/DataverseClient/Client/Model/DiscoveryServers.cs index 7d0099f..d1e173c 100644 --- a/src/GeneralTools/DataverseClient/Client/Model/DiscoveryServers.cs +++ b/src/GeneralTools/DataverseClient/Client/Model/DiscoveryServers.cs @@ -15,17 +15,17 @@ namespace Microsoft.PowerPlatform.Dataverse.Client.Model public class DiscoveryServers : INotifyPropertyChanged , IDisposable { /// - /// CRM Log entry + /// Log entry /// DataverseTraceLogger logger = null; /// - /// Contains the list of Office 365 CRM enabled Discovery Servers + /// Contains the list of Online Discovery Servers /// private ObservableCollection _OSDPServers = new ObservableCollection(); /// - /// Public Property to Access Office 365 discovery servers + /// Public Property to discovery servers /// public ObservableCollection OSDPServers { get { return _OSDPServers; } set { if (value != _OSDPServers) _OSDPServers = value; NotifyPropertyChanged("OSDPServers"); } } diff --git a/src/GeneralTools/DataverseClient/Client/Model/OrgsDictionary.cs b/src/GeneralTools/DataverseClient/Client/Model/OrgsDictionary.cs index 6189838..37ecb03 100644 --- a/src/GeneralTools/DataverseClient/Client/Model/OrgsDictionary.cs +++ b/src/GeneralTools/DataverseClient/Client/Model/OrgsDictionary.cs @@ -10,7 +10,7 @@ namespace Microsoft.PowerPlatform.Dataverse.Client.Model { /// - /// Describes a Single Organization returned from a CRM Discovery server + /// Describes a Single Organization returned from a Discovery server /// public sealed class OrgByServer : INotifyPropertyChanged { @@ -21,22 +21,22 @@ public sealed class OrgByServer : INotifyPropertyChanged /// - /// This is the display name for the organization that a user sees when working in CRM + /// This is the display name for the organization that a user sees when working in Dataverse /// public string FriendlyName { get { return _OrgDetail != null ? _OrgDetail.FriendlyName : string.Empty; } } /// - /// This is the actual name for the organization in CRM, and is required to connect to CRM + /// This is the actual name for the organization in Dataverse, and is required to connect to Dataverse /// public string UniqueOrgName { get { return _OrgDetail != null ? _OrgDetail.UniqueName : string.Empty; } } /// - /// This is the actual name for the organization in CRM, and is required to connect to CRM + /// This is the actual name for the organization in Dataverse, and is required to connect to Dataverse /// public string UrlHostName { get { return _OrgDetail != null ? _OrgDetail.UrlName : string.Empty; } } /// - /// This is the details of the Organization, returned directly from CRM + /// This is the details of the Organization, returned directly from Dataverse /// public OrganizationDetail OrgDetail { get { return _OrgDetail; } set { if (value != _OrgDetail) _OrgDetail = value; NotifyPropertyChanged("OrdDetail"); } } @@ -90,7 +90,7 @@ public sealed class OrgList : INotifyPropertyChanged public ObservableCollection OrgsList { get { return _orgsList; } internal set { if (value != _orgsList) _orgsList = value; NotifyPropertyChanged("OrgsList"); } } /// - /// Container for CRM Orgs List. + /// Container for Dataverse Orgs List. /// public OrgList() { } diff --git a/src/GeneralTools/DataverseClient/Client/Properties/AssemblyInfo.cs b/src/GeneralTools/DataverseClient/Client/Properties/AssemblyInfo.cs index 8335e83..f628d8f 100644 --- a/src/GeneralTools/DataverseClient/Client/Properties/AssemblyInfo.cs +++ b/src/GeneralTools/DataverseClient/Client/Properties/AssemblyInfo.cs @@ -24,4 +24,5 @@ [assembly: InternalsVisibleTo("DataverseClient_Core_UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("Microsoft.PowerPlatform.Dataverse.Client.Dynamics, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.PowerPlatform.Dataverse.ConnectControl, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/src/GeneralTools/DataverseClient/Client/ServiceClient.cs b/src/GeneralTools/DataverseClient/Client/ServiceClient.cs index 6028213..15c5f6e 100644 --- a/src/GeneralTools/DataverseClient/Client/ServiceClient.cs +++ b/src/GeneralTools/DataverseClient/Client/ServiceClient.cs @@ -7,7 +7,6 @@ using System.Globalization; using System.IO; using System.Linq; -using System.Net; using System.Security; using System.ServiceModel; using System.ServiceModel.Description; @@ -22,17 +21,15 @@ using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using System.Net.Http; -using Microsoft.Rest; using Microsoft.PowerPlatform.Dataverse.Client.Utils; -using Newtonsoft.Json.Linq; using Microsoft.Extensions.DependencyInjection; using Microsoft.PowerPlatform.Dataverse.Client.Auth; -using Microsoft.PowerPlatform.Dataverse.Client; using System.Threading; -using System.Dynamic; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.PowerPlatform.Dataverse.Client.Model; +using System.Reflection; +using Microsoft.Extensions.Caching.Memory; #endregion namespace Microsoft.PowerPlatform.Dataverse.Client @@ -40,7 +37,7 @@ namespace Microsoft.PowerPlatform.Dataverse.Client /// /// Primary implementation of the API interface for Dataverse. /// - public sealed class ServiceClient : IOrganizationService, IOrganizationServiceAsync, IDisposable + public sealed class ServiceClient : IOrganizationService, IOrganizationServiceAsync2, IDisposable { #region Vars @@ -125,7 +122,7 @@ public sealed class ServiceClient : IOrganizationService, IOrganizationServiceAs /// /// Exposed OrganizationWebProxyClient for consumers /// - internal OrganizationWebProxyClient OrganizationWebProxyClient + internal OrganizationWebProxyClientAsync OrganizationWebProxyClient { get { @@ -276,7 +273,7 @@ public string CurrentAccessToken _connectionSvc.AuthenticationTypeInUse == AuthenticationType.ExternalTokenManagement || _connectionSvc.AuthenticationTypeInUse == AuthenticationType.ClientSecret)) { - return _connectionSvc.RefreshWebProxyClientTokenAsync().Result; + return _connectionSvc.RefreshClientTokenAsync().Result; } else return string.Empty; @@ -325,6 +322,25 @@ internal IOrganizationService DataverseService } } + /// + /// Pointer to Dataverse Service. + /// + internal IOrganizationServiceAsync DataverseServiceAsync + { + get + { + // Added to support testing of ServiceClient direct code. + //if (_testOrgSvcInterface != null) + // return _testOrgSvcInterface; + + if (_connectionSvc != null) + { + return _connectionSvc.WebClient; + } + else return null; + } + } + /// /// Current user Record. /// @@ -535,7 +551,7 @@ public string SdkVersionProperty { if (string.IsNullOrEmpty(_sdkVersionProperty)) { - _sdkVersionProperty = FileVersionInfo.GetVersionInfo(typeof(OrganizationWebProxyClient).Assembly.Location).FileVersion; + _sdkVersionProperty = typeof(OrganizationDetail).Assembly.GetCustomAttribute().Version ?? FileVersionInfo.GetVersionInfo(typeof(OrganizationDetail).Assembly.Location).FileVersion; } return _sdkVersionProperty; } @@ -574,7 +590,8 @@ public string EnvironmentId } /// - /// Use Web API instead of legacy SOAP org service + /// Use Web API instead of org service where possible. + /// WARNING. THEASE ARE TEMPORARY SETTINGS AND WILL BE REMOVED IN THE FUTURE /// public bool UseWebApi { @@ -598,8 +615,9 @@ private ServiceClient() /// /// /// + /// /// Logging provider - internal ServiceClient(IOrganizationService orgSvc, HttpClient httpClient, Version targetVersion = null, ILogger logger = null) + internal ServiceClient(IOrganizationService orgSvc, HttpClient httpClient, string baseConnectUrl, Version targetVersion = null, ILogger logger = null) { _testOrgSvcInterface = orgSvc; _logEntry = new DataverseTraceLogger(logger) @@ -607,8 +625,7 @@ internal ServiceClient(IOrganizationService orgSvc, HttpClient httpClient, Versi LogRetentionDuration = new TimeSpan(0, 10, 0), EnabledInMemoryLogCapture = true }; - _connectionSvc = new ConnectionService(orgSvc); - _connectionSvc.WebApiHttpClient = httpClient; + _connectionSvc = new ConnectionService(orgSvc, baseConnectUrl , httpClient, logger); if (targetVersion != null) _connectionSvc.OrganizationVersion = targetVersion; @@ -631,17 +648,20 @@ public ServiceClient(string dataverseConnectionString, ILogger logger = null) ConnectToService(dataverseConnectionString, logger); } - /// - /// Uses the Organization Web proxy Client provided by the user - /// - /// User Provided Organization Web Proxy Client - /// Logging provider - public ServiceClient(OrganizationWebProxyClient externalOrgWebProxyClient, ILogger logger = null) - { - CreateServiceConnection(null, AuthenticationType.OAuth, string.Empty, string.Empty, string.Empty, null, string.Empty, - MakeSecureString(string.Empty), string.Empty, string.Empty, string.Empty, false, false, null, string.Empty, null, - PromptBehavior.Auto, externalOrgWebProxyClient, externalLogger: logger); - } + //REMOVED FROM BUILD FOR NOW + ///// + ///// Uses the Organization Web proxy Client provided by the user + ///// + ///// User Provided Organization Web Proxy Client + ///// Logging provider + //internal ServiceClient(OrganizationWebProxyClient externalOrgWebProxyClient, ILogger logger = null) + //{ + // // Disabled for this build as we determine how to support server side Native Client + + // //CreateServiceConnection(null, AuthenticationType.OAuth, string.Empty, string.Empty, string.Empty, null, string.Empty, + // // MakeSecureString(string.Empty), string.Empty, string.Empty, string.Empty, false, false, null, string.Empty, null, + // // PromptBehavior.Auto, externalOrgWebProxyClient, externalLogger: logger); + //} /// /// Creates an instance of ServiceClient who's authentication is managed by the caller. @@ -679,14 +699,15 @@ public ServiceClient(Uri instanceUrl, Func> tokenProviderFu /// The redirect URI application will be redirected post OAuth authentication. /// The prompt Behavior. /// (optional) If true attempts login using current user ( Online ) + /// (Optional)The token cache path where token cache file is placed. if string.empty, will use default cache file store, if null, will use in memory cache /// Logging provider public ServiceClient(string userId, SecureString password, string regionGeo, string orgName, bool useUniqueInstance, OrganizationDetail orgDetail, - string clientId, Uri redirectUri, PromptBehavior promptBehavior = PromptBehavior.Auto, bool useDefaultCreds = false, ILogger logger = null) + string clientId, Uri redirectUri, PromptBehavior promptBehavior = PromptBehavior.Auto, bool useDefaultCreds = false, string tokenCacheStorePath = null, ILogger logger = null) { CreateServiceConnection( null, AuthenticationType.OAuth, string.Empty, string.Empty, orgName, null, userId, password, string.Empty, regionGeo, string.Empty, true, useUniqueInstance, orgDetail, - clientId, redirectUri, promptBehavior, null, useDefaultCreds: useDefaultCreds, externalLogger: logger); + clientId, redirectUri, promptBehavior, null, useDefaultCreds: useDefaultCreds, externalLogger: logger, tokenCacheStorePath: tokenCacheStorePath); } /// @@ -703,14 +724,15 @@ public ServiceClient(string userId, SecureString password, string regionGeo, str /// The prompt Behavior. /// (optional) If true attempts login using current user ( Online ) /// API or Instance URI to access the Dataverse environment. + /// (Optional)The token cache path where token cache file is placed. if string.empty, will use default cache file store, if null, will use in memory cache /// Logging provider public ServiceClient(string userId, SecureString password, Uri hostUri, bool useUniqueInstance, - string clientId, Uri redirectUri, PromptBehavior promptBehavior = PromptBehavior.Auto, bool useDefaultCreds = false, ILogger logger = null) + string clientId, Uri redirectUri, PromptBehavior promptBehavior = PromptBehavior.Auto, bool useDefaultCreds = false, string tokenCacheStorePath = null, ILogger logger = null) { CreateServiceConnection( null, AuthenticationType.OAuth, string.Empty, string.Empty, null, null, userId, password, string.Empty, null, string.Empty, true, useUniqueInstance, null, - clientId, redirectUri, promptBehavior, null, useDefaultCreds: useDefaultCreds, instanceUrl: hostUri, externalLogger: logger); + clientId, redirectUri, promptBehavior, null, useDefaultCreds: useDefaultCreds, instanceUrl: hostUri, externalLogger: logger, tokenCacheStorePath: tokenCacheStorePath); } /// @@ -728,14 +750,15 @@ public ServiceClient(string userId, SecureString password, Uri hostUri, bool use /// The registered client Id on Azure portal. /// The redirect URI application will be redirected post OAuth authentication. /// The prompt Behavior. + /// (Optional)The token cache path where token cache file is placed. if string.empty, will use default cache file store, if null, will use in memory cache /// Logging provider public ServiceClient(string userId, SecureString password, string domain, string hostName, string port, string orgName, bool useSsl, bool useUniqueInstance, - OrganizationDetail orgDetail, string clientId, Uri redirectUri, PromptBehavior promptBehavior = PromptBehavior.Auto, ILogger logger = null) + OrganizationDetail orgDetail, string clientId, Uri redirectUri, PromptBehavior promptBehavior = PromptBehavior.Auto, string tokenCacheStorePath = null, ILogger logger = null) { CreateServiceConnection( null, AuthenticationType.OAuth, hostName, port, orgName, null, userId, password, domain, string.Empty, string.Empty, useSsl, useUniqueInstance, orgDetail, - clientId, redirectUri, promptBehavior, null, externalLogger: logger); + clientId, redirectUri, promptBehavior, null, externalLogger: logger, tokenCacheStorePath: tokenCacheStorePath); } /// @@ -751,10 +774,9 @@ public ServiceClient(string userId, SecureString password, string domain, string /// Dataverse Org Detail object, this is is returned from a query to the Dataverse Discovery Server service. not required. /// The registered client Id on Azure portal. /// The redirect URI application will be redirected post OAuth authentication. - /// The token cache path where token cache file is placed. /// Logging provider public ServiceClient(X509Certificate2 certificate, StoreName certificateStoreName, string certificateThumbPrint, Uri instanceUrl, string orgName, bool useSsl, bool useUniqueInstance, - OrganizationDetail orgDetail, string clientId, Uri redirectUri, string tokenCachePath, ILogger logger = null) + OrganizationDetail orgDetail, string clientId, Uri redirectUri, ILogger logger = null) { if ((string.IsNullOrEmpty(clientId) || redirectUri == null)) { @@ -900,7 +922,8 @@ internal void ConnectToService(string connectionString, ILogger logger = null) CreateServiceConnection(null, parsedConnStr.AuthenticationType, hostname, port, orgName, networkCredentials, userId, MakeSecureString(password), domainname, onlineRegion, homesRealm, useSsl, parsedConnStr.UseUniqueConnectionInstance, - null, clientId, redirectUri, parsedConnStr.PromptBehavior, instanceUrl: parsedConnStr.SkipDiscovery ? parsedConnStr.ServiceUri : null, useDefaultCreds: parsedConnStr.UseCurrentUser, externalLogger: logger); + null, clientId, redirectUri, parsedConnStr.PromptBehavior, instanceUrl: parsedConnStr.SkipDiscovery ? parsedConnStr.ServiceUri : null, + useDefaultCreds: parsedConnStr.UseCurrentUser, externalLogger: logger, tokenCacheStorePath: parsedConnStr.TokenCacheStorePath); break; case AuthenticationType.Certificate: hostname = parsedConnStr.IsOnPremOauth ? hostname : string.Empty; // @@ -948,7 +971,7 @@ internal void ConnectToService(string connectionString, ILogger logger = null) /// Auth type of source connection /// source organization version /// Logging provider - internal ServiceClient(OrganizationWebProxyClient externalOrgWebProxyClient, bool isCloned = true, AuthenticationType orginalAuthType = AuthenticationType.OAuth, Version sourceOrgVersion = null, ILogger logger = null) + internal ServiceClient(OrganizationWebProxyClientAsync externalOrgWebProxyClient, bool isCloned = true, AuthenticationType orginalAuthType = AuthenticationType.OAuth, Version sourceOrgVersion = null, ILogger logger = null) { CreateServiceConnection(null, orginalAuthType, string.Empty, string.Empty, string.Empty, null, string.Empty, MakeSecureString(string.Empty), string.Empty, string.Empty, string.Empty, false, false, null, string.Empty, null, @@ -986,6 +1009,7 @@ internal ServiceClient(OrganizationWebProxyClient externalOrgWebProxyClient, boo /// (optional) If true attempts login using current user ( Online ) /// Incoming Org Version, used as part of clone. /// Logging provider + /// path for token file storage internal void CreateServiceConnection( object externalOrgServiceProxy, AuthenticationType requestedAuthType, @@ -1004,7 +1028,7 @@ internal void CreateServiceConnection( string clientId = "", Uri redirectUri = null, PromptBehavior promptBehavior = PromptBehavior.Auto, - OrganizationWebProxyClient externalOrgWebProxyClient = null, + OrganizationWebProxyClientAsync externalOrgWebProxyClient = null, string certificateThumbPrint = "", StoreName certificateStoreName = StoreName.My, X509Certificate2 certificate = null, @@ -1012,7 +1036,8 @@ internal void CreateServiceConnection( bool isCloned = false, bool useDefaultCreds = false, Version incomingOrgVersion = null, - ILogger externalLogger = null + ILogger externalLogger = null, + string tokenCacheStorePath = null ) { @@ -1045,21 +1070,11 @@ internal void CreateServiceConnection( try { // Support for things like Excel that do not run from a local directory. - if (File.Exists("microsoft.cds.sdk.dll")) + Version fileVersion = new Version(SdkVersionProperty); + if (!(Utilities.FeatureVersionMinimums.IsFeatureValidForEnviroment(fileVersion, Utilities.FeatureVersionMinimums.DataverseVersionForThisAPI))) { - // Do CDS Assembly version Check... - // Must be assemblies of version 5.0.9688.1533 or newer. - FileVersionInfo fv = FileVersionInfo.GetVersionInfo("microsoft.cds.sdk.dll"); - - if (fv != null) - { - Version fileVersion = new Version(fv.ProductVersion); - if (!(Utilities.FeatureVersionMinimums.IsFeatureValidForEnviroment(fileVersion, Utilities.FeatureVersionMinimums.DataverseVersionForThisAPI))) - { - _logEntry.Log("!!WARNING!!! The version of the Dataverse product assemblies is less than the recommend version for this API; you must use version 5.0.9688.1533 or newer (Newer then the Oct-2011 service release)", TraceEventType.Warning); - _logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Dataverse Version found is {0}", fv.ProductVersion), TraceEventType.Warning); - } - } + _logEntry.Log("!!WARNING!!! The version of the Dataverse product assemblies is less than the recommend version for this API; you must use version 5.0.9688.1533 or newer (Newer then the Oct-2011 service release)", TraceEventType.Warning); + _logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Dataverse Version found is {0}", SdkVersionProperty), TraceEventType.Warning); } } catch @@ -1090,7 +1105,7 @@ internal void CreateServiceConnection( useUniqueInstance, orgDetail, clientId, redirectUri, certificateThumbPrint, - certificateStoreName, certificate, hostName, port, false, logSink: _logEntry); + certificateStoreName, certificate, hostName, port, false, logSink: _logEntry, tokenCacheStorePath: tokenCacheStorePath); if (GetAccessToken != null) _connectionSvc.GetAccessTokenAsync = GetAccessToken; @@ -1106,20 +1121,20 @@ internal void CreateServiceConnection( if (requestedAuthType == AuthenticationType.OAuth) { if (!String.IsNullOrEmpty(hostName)) - _connectionSvc = new ConnectionService(requestedAuthType, orgName, userId, password, Geo, useUniqueInstance, orgDetail, clientId, redirectUri, promptBehavior, hostName, port, true, instanceToConnectToo: instanceUrl, logSink: _logEntry, useDefaultCreds: useDefaultCreds); + _connectionSvc = new ConnectionService(requestedAuthType, orgName, userId, password, Geo, useUniqueInstance, orgDetail, clientId, redirectUri, promptBehavior, hostName, port, true, instanceToConnectToo: instanceUrl, logSink: _logEntry, useDefaultCreds: useDefaultCreds, tokenCacheStorePath: tokenCacheStorePath); else - _connectionSvc = new ConnectionService(requestedAuthType, orgName, userId, password, Geo, useUniqueInstance, orgDetail, clientId, redirectUri, promptBehavior, hostName, port, false, instanceToConnectToo: instanceUrl, logSink: _logEntry, useDefaultCreds: useDefaultCreds); + _connectionSvc = new ConnectionService(requestedAuthType, orgName, userId, password, Geo, useUniqueInstance, orgDetail, clientId, redirectUri, promptBehavior, hostName, port, false, instanceToConnectToo: instanceUrl, logSink: _logEntry, useDefaultCreds: useDefaultCreds, tokenCacheStorePath: tokenCacheStorePath); } else if (requestedAuthType == AuthenticationType.Certificate) { - _connectionSvc = new ConnectionService(requestedAuthType, instanceUrl, useUniqueInstance, orgDetail, clientId, redirectUri, certificateThumbPrint, certificateStoreName, certificate, hostName, port, !String.IsNullOrEmpty(hostName), logSink: _logEntry); + _connectionSvc = new ConnectionService(requestedAuthType, instanceUrl, useUniqueInstance, orgDetail, clientId, redirectUri, certificateThumbPrint, certificateStoreName, certificate, hostName, port, !String.IsNullOrEmpty(hostName), logSink: _logEntry, tokenCacheStorePath: tokenCacheStorePath); } else if (requestedAuthType == AuthenticationType.ClientSecret) { if (!String.IsNullOrEmpty(hostName)) - _connectionSvc = new ConnectionService(requestedAuthType, orgName, userId, password, Geo, useUniqueInstance, orgDetail, clientId, redirectUri, promptBehavior, hostName, port, true, instanceToConnectToo: instanceUrl, logSink: _logEntry, useDefaultCreds: useDefaultCreds); + _connectionSvc = new ConnectionService(requestedAuthType, orgName, userId, password, Geo, useUniqueInstance, orgDetail, clientId, redirectUri, promptBehavior, hostName, port, true, instanceToConnectToo: instanceUrl, logSink: _logEntry, useDefaultCreds: useDefaultCreds, tokenCacheStorePath: tokenCacheStorePath); else - _connectionSvc = new ConnectionService(requestedAuthType, orgName, userId, password, Geo, useUniqueInstance, orgDetail, clientId, redirectUri, promptBehavior, hostName, port, false, instanceToConnectToo: instanceUrl, logSink: _logEntry, useDefaultCreds: useDefaultCreds); + _connectionSvc = new ConnectionService(requestedAuthType, orgName, userId, password, Geo, useUniqueInstance, orgDetail, clientId, redirectUri, promptBehavior, hostName, port, false, instanceToConnectToo: instanceUrl, logSink: _logEntry, useDefaultCreds: useDefaultCreds, tokenCacheStorePath: tokenCacheStorePath); } } } @@ -1133,7 +1148,7 @@ internal void CreateServiceConnection( _connectionSvc.InternetProtocalToUse = useSsl ? "https" : "http"; if (!_connectionSvc.DoLogin(out tempConnectService)) { - this._logEntry.Log("Unable to Login to Dataverse", TraceEventType.Error); + _logEntry.Log("Unable to Login to Dataverse", TraceEventType.Error); IsReady = false; return; } @@ -1159,7 +1174,7 @@ internal void CreateServiceConnection( } catch (Exception ex) { - throw new Utils.DataverseConnectionException("Failed to connect to Dataverse", ex); + throw new DataverseConnectionException("Failed to connect to Dataverse", ex); } } } @@ -1214,13 +1229,13 @@ public ServiceClient Clone(System.Reflection.Assembly strongTypeAsm, ILogger log return null; } - OrganizationWebProxyClient proxy = null; + OrganizationWebProxyClientAsync proxy = null; if (_connectionSvc.ConnectOrgUriActual != null) { if (strongTypeAsm == null) - proxy = new OrganizationWebProxyClient(_connectionSvc.ConnectOrgUriActual, true); + proxy = new OrganizationWebProxyClientAsync(_connectionSvc.ConnectOrgUriActual, true); else - proxy = new OrganizationWebProxyClient(_connectionSvc.ConnectOrgUriActual, strongTypeAsm); + proxy = new OrganizationWebProxyClientAsync(_connectionSvc.ConnectOrgUriActual, strongTypeAsm); } else { @@ -1228,9 +1243,9 @@ public ServiceClient Clone(System.Reflection.Assembly strongTypeAsm, ILogger log if (orgWebClient != null) { if (strongTypeAsm == null) - proxy = new OrganizationWebProxyClient(orgWebClient.Endpoint.Address.Uri, true); + proxy = new OrganizationWebProxyClientAsync(orgWebClient.Endpoint.Address.Uri, true); else - proxy = new OrganizationWebProxyClient(orgWebClient.Endpoint.Address.Uri, strongTypeAsm); + proxy = new OrganizationWebProxyClientAsync(orgWebClient.Endpoint.Address.Uri, strongTypeAsm); } else { @@ -1262,6 +1277,7 @@ public ServiceClient Clone(System.Reflection.Assembly strongTypeAsm, ILogger log } catch (Exception ex) { + _logEntry.Log(ex); throw new DataverseConnectionException("Failed to Clone Connection", ex); } } @@ -1284,11 +1300,12 @@ public ServiceClient Clone(System.Reflection.Assembly strongTypeAsm, ILogger log /// The prompt behavior. /// The authority provider for OAuth tokens. Unique if any already known. /// (Optional) if specified, tries to use the current user + /// (optional) path to log store /// Logging provider /// A collection of organizations - public static async Task DiscoverOnPremiseOrganizationsAsync(Uri discoveryServiceUri, ClientCredentials clientCredentials, string clientId, Uri redirectUri, string authority, PromptBehavior promptBehavior = PromptBehavior.Auto, bool useDefaultCreds = false, ILogger logger = null) + public static async Task DiscoverOnPremiseOrganizationsAsync(Uri discoveryServiceUri, ClientCredentials clientCredentials, string clientId, Uri redirectUri, string authority, PromptBehavior promptBehavior = PromptBehavior.Auto, bool useDefaultCreds = false, string tokenCacheStorePath = null, ILogger logger = null) { - return await ConnectionService.DiscoverOrganizationsAsync(discoveryServiceUri, clientCredentials, clientId, redirectUri, promptBehavior, isOnPrem: true, authority, useDefaultCreds: useDefaultCreds, externalLogger: logger).ConfigureAwait(false); + return await ConnectionService.DiscoverOrganizationsAsync(discoveryServiceUri, clientCredentials, clientId, redirectUri, promptBehavior, isOnPrem: true, authority, useDefaultCreds: useDefaultCreds, externalLogger: logger, tokenCacheStorePath: tokenCacheStorePath).ConfigureAwait(false); } /// @@ -1302,11 +1319,12 @@ public static async Task DiscoverOnPremiseOrganiza /// The deployment type: OnPrem or Online. /// The authority provider for OAuth tokens. Unique if any already known. /// (Optional) if specified, tries to use the current user + /// (optional) path to log store /// Logging provider /// A collection of organizations - public static async Task DiscoverOnlineOrganizationsAsync(Uri discoveryServiceUri, ClientCredentials clientCredentials, string clientId, Uri redirectUri, bool isOnPrem, string authority, PromptBehavior promptBehavior = PromptBehavior.Auto, bool useDefaultCreds = false, ILogger logger = null) + public static async Task DiscoverOnlineOrganizationsAsync(Uri discoveryServiceUri, ClientCredentials clientCredentials, string clientId, Uri redirectUri, bool isOnPrem, string authority, PromptBehavior promptBehavior = PromptBehavior.Auto, bool useDefaultCreds = false, string tokenCacheStorePath = null, ILogger logger = null) { - return await ConnectionService.DiscoverOrganizationsAsync(discoveryServiceUri, clientCredentials, clientId, redirectUri, promptBehavior, isOnPrem, authority, useGlobalDisco: true, useDefaultCreds: useDefaultCreds, externalLogger: logger).ConfigureAwait(false); + return await ConnectionService.DiscoverOrganizationsAsync(discoveryServiceUri, clientCredentials, clientId, redirectUri, promptBehavior, isOnPrem, authority, useGlobalDisco: true, useDefaultCreds: useDefaultCreds, externalLogger: logger, tokenCacheStorePath: tokenCacheStorePath).ConfigureAwait(false); } /// @@ -1323,9 +1341,10 @@ public static async Task DiscoverOnlineOrganizatio /// The deployment type: OnPrem or Online. /// The authority provider for OAuth tokens. Unique if any already known. /// (Optional) if specified, tries to use the current user + /// (optional) path to log store /// Logging provider /// A collection of organizations - public static async Task DiscoverOnlineOrganizationsAsync(string userId, string password, string clientId, Uri redirectUri, bool isOnPrem, string authority, PromptBehavior promptBehavior = PromptBehavior.Auto, bool useDefaultCreds = false, Model.DiscoveryServer discoServer = null, ILogger logger = null) + public static async Task DiscoverOnlineOrganizationsAsync(string userId, string password, string clientId, Uri redirectUri, bool isOnPrem, string authority, PromptBehavior promptBehavior = PromptBehavior.Auto, bool useDefaultCreds = false, Model.DiscoveryServer discoServer = null, string tokenCacheStorePath = null, ILogger logger = null) { Uri discoveryUriToUse = null; if (discoServer != null && discoServer.RequiresRegionalDiscovery) @@ -1344,7 +1363,7 @@ public static async Task DiscoverOnlineOrganizatio clientCredentials.UserName.UserName = userId; clientCredentials.UserName.Password = password; - return await ConnectionService.DiscoverOrganizationsAsync(discoveryUriToUse, clientCredentials, clientId, redirectUri, promptBehavior, isOnPrem, authority, useGlobalDisco: true, useDefaultCreds: useDefaultCreds, externalLogger: logger).ConfigureAwait(false); + return await ConnectionService.DiscoverOrganizationsAsync(discoveryUriToUse, clientCredentials, clientId, redirectUri, promptBehavior, isOnPrem, authority, useGlobalDisco: true, useDefaultCreds: useDefaultCreds, externalLogger: logger, tokenCacheStorePath: tokenCacheStorePath).ConfigureAwait(false); } /// @@ -1352,14 +1371,15 @@ public static async Task DiscoverOnlineOrganizatio /// /// Global discovery base URI to use to connect too, if null will utilize the commercial Global Discovery Server. /// Function that will provide access token to the discovery call. + /// (optional) path to log store /// Logging provider /// - public static async Task DiscoverOnlineOrganizationsAsync(Func> tokenProviderFunction, Uri discoveryServiceUri = null, ILogger logger = null) + public static async Task DiscoverOnlineOrganizationsAsync(Func> tokenProviderFunction, Uri discoveryServiceUri = null, string tokenCacheStorePath = null, ILogger logger = null) { if (discoveryServiceUri == null) discoveryServiceUri = new Uri(ConnectionService.GlobalDiscoveryAllInstancesUri); // use commercial GD - return await ConnectionService.DiscoverGlobalOrganizationsAsync(discoveryServiceUri, tokenProviderFunction, externalLogger: logger).ConfigureAwait(false); + return await ConnectionService.DiscoverGlobalOrganizationsAsync(discoveryServiceUri, tokenProviderFunction, externalLogger: logger, tokenCacheStorePath: tokenCacheStorePath).ConfigureAwait(false); } #endregion @@ -4679,8 +4699,10 @@ private Guid GetLookupValueForEntity(string entName, string Value) // Check for existence of cached list. if (_CachObject == null) { + object objc = _connectionSvc.LocalMemoryCache.Get(_cachObjecName); + if (objc is Dictionary> workingObj) + _CachObject = workingObj; - _CachObject = (Dictionary>)System.Runtime.Caching.MemoryCache.Default[_cachObjecName]; if (_CachObject == null) _CachObject = new Dictionary>(); } @@ -4723,7 +4745,7 @@ private Guid GetLookupValueForEntity(string entName, string Value) _CachObject.Add(entName.ToString(), new Dictionary()); _CachObject[entName.ToString()].Add(Value, guResultID); - System.Runtime.Caching.MemoryCache.Default.Add(_cachObjecName, _CachObject, DateTime.Now.AddMinutes(5)); + _connectionSvc.LocalMemoryCache.Set(_cachObjecName, _CachObject, DateTime.Now.AddMinutes(5)); } return guResultID; @@ -4982,7 +5004,7 @@ private OrganizationResponse ExecuteOrganizationRequestImpl(OrganizationRequest { if (req != null) { - useWebAPI = Utilities.IsRequestValidForTranslationToWebAPI(req, UseWebApi) ? useWebAPI : false; + useWebAPI = Utilities.IsRequestValidForTranslationToWebAPI(req); if (!useWebAPI) { return Command_Execute(req, logMessageTag, bypassPluginExecution); @@ -5004,7 +5026,7 @@ private async Task ExecuteOrganizationRequestAsyncImpl(Org { if (req != null) { - useWebAPI = Utilities.IsRequestValidForTranslationToWebAPI(req, UseWebApi) ? useWebAPI : false; + useWebAPI = Utilities.IsRequestValidForTranslationToWebAPI(req); if (!useWebAPI) { return await Command_ExecuteAsync(req, logMessageTag, cancellationToken, bypassPluginExecution).ConfigureAwait(false); @@ -5362,9 +5384,112 @@ internal Guid ImportSolutionToImpl(string solutionPath, out Guid importId, bool /// Result of create request or null. internal async Task Command_ExecuteAsync(OrganizationRequest req, string errorStringCheck, System.Threading.CancellationToken cancellationToken, bool bypassPluginExecution = false) { - return await Task.Run(() => Command_Execute(req, errorStringCheck, bypassPluginExecution), cancellationToken).ConfigureAwait(false); + if (DataverseServiceAsync != null) + { + // if created based on Async Client. + return await Command_ExecuteAsyncImpl(req, errorStringCheck, cancellationToken, bypassPluginExecution).ConfigureAwait(false); + } + else + { + // if not use task.run(). + return await Task.Run(() => Command_Execute(req, errorStringCheck, bypassPluginExecution), cancellationToken).ConfigureAwait(false); + } + } + /// + /// Executes a Dataverse Create Request and returns the organization response object. + /// + /// Request to run + /// Formatted Error string + /// Adds the bypass plugin behavior to this request. Note: this will only apply if the caller has the prvBypassPlugins permission to bypass plugins. If its attempted without the permission the request will fault. + /// + /// Result of create request or null. + internal async Task Command_ExecuteAsyncImpl(OrganizationRequest req, string errorStringCheck, System.Threading.CancellationToken cancellationToken, bool bypassPluginExecution = false) + { + Guid requestTrackingId = Guid.NewGuid(); + OrganizationResponse resp = null; + Stopwatch logDt = new Stopwatch(); + TimeSpan LockWait = TimeSpan.Zero; + int retryCount = 0; + bool retry = false; + + do + { + try + { + _retryPauseTimeRunning = _configuration.Value.RetryPauseTime; + retry = false; + if (!_disableConnectionLocking) + if (_lockObject == null) + _lockObject = new object(); + + if (_connectionSvc != null && _connectionSvc.AuthenticationTypeInUse == AuthenticationType.OAuth) + _connectionSvc.CalledbyExecuteRequest = true; + OrganizationResponse rsp = null; + + // Check to see if a Tracking ID has allready been assigned, + if (!req.RequestId.HasValue || (req.RequestId.HasValue && req.RequestId.Value == Guid.Empty)) + { + // Assign Tracking ID + req.RequestId = requestTrackingId; + } + else + { + // assign request Id to the tracking id. + requestTrackingId = req.RequestId.Value; + } + + // if request should bypass plugin exec. + if (bypassPluginExecution && Utilities.FeatureVersionMinimums.IsFeatureValidForEnviroment(_connectionSvc?.OrganizationVersion, Utilities.FeatureVersionMinimums.AllowBypassCustomPlugin)) + req.Parameters.Add(Utilities.RequestHeaders.BYPASSCUSTOMPLUGINEXECUTION, true); + + _logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Execute Command - {0}{1}: RequestID={2} {3}", + req.RequestName, + string.IsNullOrEmpty(errorStringCheck) ? "" : $" : {errorStringCheck} ", + requestTrackingId.ToString(), + SessionTrackingId.HasValue && SessionTrackingId.Value != Guid.Empty ? $"SessionID={SessionTrackingId.Value.ToString()} : " : "" + ), TraceEventType.Verbose); + + logDt.Restart(); + rsp = await DataverseServiceAsync.ExecuteAsync(req); + + logDt.Stop(); + _logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Executed Command - {0}{2}: {5}RequestID={3} {4}: duration={1}", + req.RequestName, + logDt.Elapsed.ToString(), + string.IsNullOrEmpty(errorStringCheck) ? "" : $" : {errorStringCheck} ", + requestTrackingId.ToString(), + LockWait == TimeSpan.Zero ? string.Empty : string.Format(": LockWaitDuration={0} ", LockWait.ToString()), + SessionTrackingId.HasValue && SessionTrackingId.Value != Guid.Empty ? $"SessionID={SessionTrackingId.Value.ToString()} : " : "" + ), TraceEventType.Verbose); + resp = rsp; + } + catch (Exception ex) + { + bool isThrottled = false; + retry = ShouldRetry(req, ex, retryCount, out isThrottled); + if (retry) + { + Utilities.RetryRequest(req, requestTrackingId, LockWait, logDt, _logEntry, SessionTrackingId, _disableConnectionLocking, _retryPauseTimeRunning, ex, errorStringCheck, ref retryCount, isThrottled); + } + else + { + _logEntry.LogRetry(retryCount, req, _retryPauseTimeRunning, true, isThrottled: isThrottled); + _logEntry.LogException(req, ex, errorStringCheck); + //keep it in end so that LastError could be a better message. + _logEntry.LogFailure(req, requestTrackingId, SessionTrackingId, _disableConnectionLocking, LockWait, logDt, ex, errorStringCheck, true); + } + resp = null; + } + finally + { + logDt.Stop(); + } + } while (retry); + + return resp; + } /// /// Executes a Dataverse Create Request and returns the organization response object. diff --git a/src/GeneralTools/DataverseClient/Client/TraceLoggerBase.cs b/src/GeneralTools/DataverseClient/Client/TraceLoggerBase.cs index e2a2fe0..c97a2e8 100644 --- a/src/GeneralTools/DataverseClient/Client/TraceLoggerBase.cs +++ b/src/GeneralTools/DataverseClient/Client/TraceLoggerBase.cs @@ -101,7 +101,7 @@ protected string TraceSourceName #region Properties /// - /// Last Error from CRM + /// Last Error from Dataverse /// public string LastError { @@ -109,7 +109,7 @@ public string LastError set { _lastError = value; } } /// - /// Last Exception from CRM + /// Last Exception from Dataverse /// public Exception LastException { diff --git a/src/GeneralTools/DataverseClient/Client/Utils/DataverseConnectionStringProcessor.cs b/src/GeneralTools/DataverseClient/Client/Utils/DataverseConnectionStringProcessor.cs index 503c378..5e68a95 100644 --- a/src/GeneralTools/DataverseClient/Client/Utils/DataverseConnectionStringProcessor.cs +++ b/src/GeneralTools/DataverseClient/Client/Utils/DataverseConnectionStringProcessor.cs @@ -13,416 +13,416 @@ namespace Microsoft.PowerPlatform.Dataverse.Client { - /// - /// Stores Parsed connection info from the use of a CDS connection string. - /// This is only populated when the CDS Connection string object is used, this is read only. - /// - internal class DataverseConnectionStringProcessor - { - /// - /// Sample / stand-in appID used when replacing O365 Auth - /// - internal static string sampleClientId = "51f81489-12ee-4a9e-aaae-a2591f45987d"; - /// - /// Sample / stand-in redirect URI used when replacing o365 Auth - /// - internal static string sampleRedirectUrl = "app://58145B91-0C36-4500-8554-080854F2AC97"; - - /// - /// URL of the Service being connected too. - /// - public Uri ServiceUri - { - get; - internal set; - } - - /// - /// Authentication Type being used for this connection - /// - public AuthenticationType AuthenticationType - { - get; - internal set; - } - - /// - /// OAuth Prompt behavior. - /// - public PromptBehavior PromptBehavior - { - get; - internal set; - } - - /// - /// Claims based Delegated Authentication Url. - /// - public Uri HomeRealmUri - { - get; - internal set; - } - - /// - /// Client credentials parsed from connection string - /// - public ClientCredentials ClientCredentials - { - get; - internal set; - } - - ///// - ///// OAuth User Identifier - ///// - //public UserIdentifier UserIdentifier - //{ - // get; - // internal set; - //} - - /// - /// Domain of User - /// Active Directory Auth only. - /// - public string DomainName - { - get; - internal set; - } - - /// - /// User ID of the User connection to CDS - /// - public string UserId - { - get; - internal set; - } - - /// - /// Password of user, parsed from connection string - /// - internal string Password - { - get; - set; - } - - /// - /// Certificate Store Name - /// - internal string CertStoreName { get; set; } - - /// - /// Cert Thumbprint ID - /// - internal string CertThumbprint { get; set; } - - /// - /// if set to true, then the org URI should be used directly. - /// - internal bool SkipDiscovery { get; set; } - - /// - /// Client ID used in the connection string - /// - public string ClientId - { - get; - internal set; - } - - /// - /// Client Secret passed from the connection string - /// - public string ClientSecret - { - get; - internal set; - } - - /// - /// Organization Name parsed from the connection string. - /// - public string Organization - { - get; - internal set; - } - - /// - /// Set if the connection string is for an onPremise connection - /// - public bool IsOnPremOauth - { - get; - internal set; - } - - /// - /// CDS region determined by the connection string - /// - public string Geo - { - get; - internal set; - } - - /// - /// OAuth Redirect URI - /// - public Uri RedirectUri - { - get; - internal set; - } - - /// - /// OAuth Token Store Path - /// - public string TokenCacheStorePath - { - get; - internal set; - } - - /// - /// When true, specifies a unique instance of the connection should be created. - /// - public bool UseUniqueConnectionInstance { get; internal set; } - - /// - /// When set to true and oAuth Mode ( not Cert ) attempts to run the login using the current user identity. - /// - public bool UseCurrentUser { get; set; } - - public DataverseConnectionStringProcessor() - { - } - - private DataverseConnectionStringProcessor(IDictionary connection , ILogger logger) - : this( - connection.FirstNotNullOrEmpty(ConnectionStringConstants.ServiceUri), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.UserName), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.Password), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.Domain), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.HomeRealmUri), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.AuthType), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.RequireNewInstance), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.ClientId), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.RedirectUri), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.TokenCacheStorePath), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.LoginPrompt), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.CertStoreName), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.CertThumbprint), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.SkipDiscovery), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.IntegratedSecurity), - connection.FirstNotNullOrEmpty(ConnectionStringConstants.ClientSecret), - logger - ) - { - } - private DataverseConnectionStringProcessor(string serviceUri, string userName, string password, string domain, string homeRealmUri, string authType, string requireNewInstance, string clientId, string redirectUri, - string tokenCacheStorePath, string loginPrompt, string certStoreName, string certThumbprint, string skipDiscovery, string IntegratedSecurity , string clientSecret , ILogger logger) - { - DataverseTraceLogger logEntry = new DataverseTraceLogger(); - Uri _serviceuriName, _realmUri; - - bool tempbool = false; - if (bool.TryParse(skipDiscovery, out tempbool)) - SkipDiscovery = tempbool; - else - SkipDiscovery = true; // changed to change defaulting behavior of skip discovery. - - - ServiceUri = GetValidUri(serviceUri, out _serviceuriName) ? _serviceuriName : null; - HomeRealmUri = GetValidUri(homeRealmUri, out _realmUri) ? _realmUri : null; - DomainName = !string.IsNullOrWhiteSpace(domain) ? domain : string.Empty; - UserId = !string.IsNullOrWhiteSpace(userName) ? userName : string.Empty; - Password = !string.IsNullOrWhiteSpace(password) ? password : string.Empty; - ClientId = !string.IsNullOrWhiteSpace(clientId) ? clientId : string.Empty; - ClientSecret = !string.IsNullOrWhiteSpace(clientSecret) ? clientSecret : string.Empty; - TokenCacheStorePath = !string.IsNullOrWhiteSpace(tokenCacheStorePath) ? tokenCacheStorePath : string.Empty; - RedirectUri = ((Uri.IsWellFormedUriString(redirectUri, UriKind.RelativeOrAbsolute)) ? new Uri(redirectUri) : null); - CertStoreName = certStoreName; - CertThumbprint = certThumbprint; - - // Check to see if use current user is configured. - bool _IntegratedSecurity = false; - if (!string.IsNullOrEmpty(IntegratedSecurity)) - bool.TryParse(IntegratedSecurity, out _IntegratedSecurity); - - bool useUniqueConnection = true; // Set default to true to follow the old behavior. - if (!string.IsNullOrEmpty(requireNewInstance)) - bool.TryParse(requireNewInstance, out useUniqueConnection); - UseUniqueConnectionInstance = useUniqueConnection; - - //UserIdentifier = !string.IsNullOrWhiteSpace(UserId) ? new UserIdentifier(UserId, UserIdentifierType.OptionalDisplayableId) : null; - - AuthenticationType authenticationType; - if (Enum.TryParse(authType, out authenticationType)) - { - AuthenticationType = authenticationType; - } - else - { - logEntry?.Log($"Authentication Type \"{authType}\" is not a valid Authentication Type.", System.Diagnostics.TraceEventType.Error); - AuthenticationType = AuthenticationType.InvalidConnection; - } - - PromptBehavior loginBehavior; - if (Enum.TryParse(loginPrompt, out loginBehavior)) - { - PromptBehavior = loginBehavior; - } - else - { - PromptBehavior = PromptBehavior.Auto; - } - - if (ServiceUri != null) - { - SetOrgnameAndOnlineRegion(ServiceUri); - } - - //if the client Id was not passed, use Sample AppID - if (string.IsNullOrWhiteSpace(ClientId)) - { - logEntry.Log($"Client ID not supplied, using SDK Sample Client ID for this connection", System.Diagnostics.TraceEventType.Warning); - ClientId = sampleClientId;// sample client ID - if (RedirectUri == null) - RedirectUri = new Uri(sampleRedirectUrl); // Sample app Redirect URI - } - - if (!string.IsNullOrWhiteSpace(userName) && !string.IsNullOrWhiteSpace(password)) - { - ClientCredentials clientCredentials = new ClientCredentials(); - clientCredentials.UserName.UserName = userName; - clientCredentials.UserName.Password = password; - ClientCredentials = clientCredentials; - - } - - logEntry.Dispose(); - - } - - private bool GetValidUri(string uriSource, out Uri validUriResult) - { - - bool validuri = Uri.TryCreate(uriSource, UriKind.Absolute, out validUriResult) && - (validUriResult.Scheme == Uri.UriSchemeHttp || validUriResult.Scheme == Uri.UriSchemeHttps); - - - return validuri; - } - /// - /// Get the organization name and online region from the org - /// - /// - private void SetOrgnameAndOnlineRegion(Uri serviceUri) - { - // uses publicaly exposed connection parser to parse - string orgRegion = string.Empty; - string orgName = string.Empty; - bool isOnPrem = false; - Utilities.GetOrgnameAndOnlineRegionFromServiceUri(serviceUri, out orgRegion, out orgName, out isOnPrem); - Geo = orgRegion; - Organization = orgName; - IsOnPremOauth = isOnPrem; - } - - - /// - /// Parse the connection sting - /// - /// - /// Logging provider - /// - public static DataverseConnectionStringProcessor Parse(string connectionString , ILogger logger = null) - { - return new DataverseConnectionStringProcessor(connectionString.ToDictionary(), logger); - } - - } - - /// - /// Extension - /// - public static class Extension - { - /// - /// Enum extension - /// - /// - /// - /// Enum Value - public static T ToEnum(this string enumName) - { - return (T)((object)Enum.Parse(typeof(T), enumName)); - } - /// - /// Converts a int to a Enum of the requested type (T) - /// - /// Enum Type to translate too - /// Int Value too translate. - /// Enum of Type T - public static T ToEnum(this int enumValue) - { - return enumValue.ToString().ToEnum(); - } - /// - /// Converts a ; separated string into a dictionary - /// - /// String to parse - /// Dictionary of properties from the connection string - public static IDictionary ToDictionary(this string connectionString) - { - try - { - DbConnectionStringBuilder source = new DbConnectionStringBuilder - { - ConnectionString = connectionString - }; - - Dictionary dictionary = source.Cast>(). - ToDictionary((KeyValuePair pair) => pair.Key, - (KeyValuePair pair) => pair.Value != null ? pair.Value.ToString() : string.Empty); - return new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); - } - catch - { - //ignore - } - return new Dictionary(); - - } - /// - /// Extension to support formating a string - /// - /// Formatting pattern - /// Argument collection - /// Formated String - public static string FormatWith(this string format, params object[] args) - { - return format.FormatWith(CultureInfo.InvariantCulture, args); - } - /// - /// Extension to get the first item in a dictionary if the dictionary contains the key. - /// - /// Type to return - /// Dictionary to search - /// Collection of Keys to find. - /// - public static string FirstNotNullOrEmpty(this IDictionary dictionary, params TKey[] keys) - { - return ( - from key in keys - where dictionary.ContainsKey(key) && !string.IsNullOrEmpty(dictionary[key]) - select dictionary[key]).FirstOrDefault(); - } - - } + /// + /// Stores Parsed connection info from the use of a CDS connection string. + /// This is only populated when the CDS Connection string object is used, this is read only. + /// + internal class DataverseConnectionStringProcessor + { + /// + /// Sample / stand-in appID used when replacing O365 Auth + /// + internal static string sampleClientId = "51f81489-12ee-4a9e-aaae-a2591f45987d"; + /// + /// Sample / stand-in redirect URI used when replacing o365 Auth + /// + internal static string sampleRedirectUrl = "app://58145B91-0C36-4500-8554-080854F2AC97"; + + /// + /// URL of the Service being connected too. + /// + public Uri ServiceUri + { + get; + internal set; + } + + /// + /// Authentication Type being used for this connection + /// + public AuthenticationType AuthenticationType + { + get; + internal set; + } + + /// + /// OAuth Prompt behavior. + /// + public PromptBehavior PromptBehavior + { + get; + internal set; + } + + /// + /// Claims based Delegated Authentication Url. + /// + public Uri HomeRealmUri + { + get; + internal set; + } + + /// + /// Client credentials parsed from connection string + /// + public ClientCredentials ClientCredentials + { + get; + internal set; + } + + ///// + ///// OAuth User Identifier + ///// + //public UserIdentifier UserIdentifier + //{ + // get; + // internal set; + //} + + /// + /// Domain of User + /// Active Directory Auth only. + /// + public string DomainName + { + get; + internal set; + } + + /// + /// User ID of the User connection to CDS + /// + public string UserId + { + get; + internal set; + } + + /// + /// Password of user, parsed from connection string + /// + internal string Password + { + get; + set; + } + + /// + /// Certificate Store Name + /// + internal string CertStoreName { get; set; } + + /// + /// Cert Thumbprint ID + /// + internal string CertThumbprint { get; set; } + + /// + /// if set to true, then the org URI should be used directly. + /// + internal bool SkipDiscovery { get; set; } + + /// + /// Client ID used in the connection string + /// + public string ClientId + { + get; + internal set; + } + + /// + /// Client Secret passed from the connection string + /// + public string ClientSecret + { + get; + internal set; + } + + /// + /// Organization Name parsed from the connection string. + /// + public string Organization + { + get; + internal set; + } + + /// + /// Set if the connection string is for an onPremise connection + /// + public bool IsOnPremOauth + { + get; + internal set; + } + + /// + /// CDS region determined by the connection string + /// + public string Geo + { + get; + internal set; + } + + /// + /// OAuth Redirect URI + /// + public Uri RedirectUri + { + get; + internal set; + } + + /// + /// OAuth Token Store Path + /// + public string TokenCacheStorePath + { + get; + internal set; + } + + /// + /// When true, specifies a unique instance of the connection should be created. + /// + public bool UseUniqueConnectionInstance { get; internal set; } + + /// + /// When set to true and oAuth Mode ( not Cert ) attempts to run the login using the current user identity. + /// + public bool UseCurrentUser { get; set; } + + public DataverseConnectionStringProcessor() + { + } + + private DataverseConnectionStringProcessor(IDictionary connection, ILogger logger) + : this( + connection.FirstNotNullOrEmpty(ConnectionStringConstants.ServiceUri), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.UserName), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.Password), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.Domain), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.HomeRealmUri), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.AuthType), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.RequireNewInstance), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.ClientId), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.RedirectUri), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.TokenCacheStorePath), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.LoginPrompt), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.CertStoreName), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.CertThumbprint), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.SkipDiscovery), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.IntegratedSecurity), + connection.FirstNotNullOrEmpty(ConnectionStringConstants.ClientSecret), + logger + ) + { + } + private DataverseConnectionStringProcessor(string serviceUri, string userName, string password, string domain, string homeRealmUri, string authType, string requireNewInstance, string clientId, string redirectUri, + string tokenCacheStorePath, string loginPrompt, string certStoreName, string certThumbprint, string skipDiscovery, string IntegratedSecurity, string clientSecret, ILogger logger) + { + DataverseTraceLogger logEntry = new DataverseTraceLogger(); + Uri _serviceuriName, _realmUri; + + bool tempbool = false; + if (bool.TryParse(skipDiscovery, out tempbool)) + SkipDiscovery = tempbool; + else + SkipDiscovery = true; // changed to change defaulting behavior of skip discovery. + + + ServiceUri = GetValidUri(serviceUri, out _serviceuriName) ? _serviceuriName : null; + HomeRealmUri = GetValidUri(homeRealmUri, out _realmUri) ? _realmUri : null; + DomainName = !string.IsNullOrWhiteSpace(domain) ? domain : string.Empty; + UserId = !string.IsNullOrWhiteSpace(userName) ? userName : string.Empty; + Password = !string.IsNullOrWhiteSpace(password) ? password : string.Empty; + ClientId = !string.IsNullOrWhiteSpace(clientId) ? clientId : string.Empty; + ClientSecret = !string.IsNullOrWhiteSpace(clientSecret) ? clientSecret : string.Empty; + TokenCacheStorePath = !string.IsNullOrWhiteSpace(tokenCacheStorePath) ? tokenCacheStorePath : null; + RedirectUri = ((Uri.IsWellFormedUriString(redirectUri, UriKind.RelativeOrAbsolute)) ? new Uri(redirectUri) : null); + CertStoreName = certStoreName; + CertThumbprint = certThumbprint; + + // Check to see if use current user is configured. + bool _IntegratedSecurity = false; + if (!string.IsNullOrEmpty(IntegratedSecurity)) + bool.TryParse(IntegratedSecurity, out _IntegratedSecurity); + + bool useUniqueConnection = true; // Set default to true to follow the old behavior. + if (!string.IsNullOrEmpty(requireNewInstance)) + bool.TryParse(requireNewInstance, out useUniqueConnection); + UseUniqueConnectionInstance = useUniqueConnection; + + //UserIdentifier = !string.IsNullOrWhiteSpace(UserId) ? new UserIdentifier(UserId, UserIdentifierType.OptionalDisplayableId) : null; + + AuthenticationType authenticationType; + if (Enum.TryParse(authType, out authenticationType)) + { + AuthenticationType = authenticationType; + } + else + { + logEntry?.Log($"Authentication Type \"{authType}\" is not a valid Authentication Type.", System.Diagnostics.TraceEventType.Error); + AuthenticationType = AuthenticationType.InvalidConnection; + } + + PromptBehavior loginBehavior; + if (Enum.TryParse(loginPrompt, out loginBehavior)) + { + PromptBehavior = loginBehavior; + } + else + { + PromptBehavior = PromptBehavior.Auto; + } + + if (ServiceUri != null) + { + SetOrgnameAndOnlineRegion(ServiceUri); + } + + //if the client Id was not passed, use Sample AppID + if (string.IsNullOrWhiteSpace(ClientId)) + { + logEntry.Log($"Client ID not supplied, using SDK Sample Client ID for this connection", System.Diagnostics.TraceEventType.Warning); + ClientId = sampleClientId;// sample client ID + if (RedirectUri == null) + RedirectUri = new Uri(sampleRedirectUrl); // Sample app Redirect URI + } + + if (!string.IsNullOrWhiteSpace(userName) && !string.IsNullOrWhiteSpace(password)) + { + ClientCredentials clientCredentials = new ClientCredentials(); + clientCredentials.UserName.UserName = userName; + clientCredentials.UserName.Password = password; + ClientCredentials = clientCredentials; + + } + + logEntry.Dispose(); + + } + + private bool GetValidUri(string uriSource, out Uri validUriResult) + { + + bool validuri = Uri.TryCreate(uriSource, UriKind.Absolute, out validUriResult) && + (validUriResult.Scheme == Uri.UriSchemeHttp || validUriResult.Scheme == Uri.UriSchemeHttps); + + + return validuri; + } + /// + /// Get the organization name and online region from the org + /// + /// + private void SetOrgnameAndOnlineRegion(Uri serviceUri) + { + // uses publicaly exposed connection parser to parse + string orgRegion = string.Empty; + string orgName = string.Empty; + bool isOnPrem = false; + Utilities.GetOrgnameAndOnlineRegionFromServiceUri(serviceUri, out orgRegion, out orgName, out isOnPrem); + Geo = orgRegion; + Organization = orgName; + IsOnPremOauth = isOnPrem; + } + + + /// + /// Parse the connection sting + /// + /// + /// Logging provider + /// + public static DataverseConnectionStringProcessor Parse(string connectionString, ILogger logger = null) + { + return new DataverseConnectionStringProcessor(connectionString.ToDictionary(), logger); + } + + } + + /// + /// Extension + /// + public static class Extension + { + /// + /// Enum extension + /// + /// + /// + /// Enum Value + public static T ToEnum(this string enumName) + { + return (T)((object)Enum.Parse(typeof(T), enumName)); + } + /// + /// Converts a int to a Enum of the requested type (T) + /// + /// Enum Type to translate too + /// Int Value too translate. + /// Enum of Type T + public static T ToEnum(this int enumValue) + { + return enumValue.ToString().ToEnum(); + } + /// + /// Converts a ; separated string into a dictionary + /// + /// String to parse + /// Dictionary of properties from the connection string + public static IDictionary ToDictionary(this string connectionString) + { + try + { + DbConnectionStringBuilder source = new DbConnectionStringBuilder + { + ConnectionString = connectionString + }; + + Dictionary dictionary = source.Cast>(). + ToDictionary((KeyValuePair pair) => pair.Key, + (KeyValuePair pair) => pair.Value != null ? pair.Value.ToString() : string.Empty); + return new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); + } + catch + { + //ignore + } + return new Dictionary(); + + } + /// + /// Extension to support formating a string + /// + /// Formatting pattern + /// Argument collection + /// Formated String + public static string FormatWith(this string format, params object[] args) + { + return format.FormatWith(CultureInfo.InvariantCulture, args); + } + /// + /// Extension to get the first item in a dictionary if the dictionary contains the key. + /// + /// Type to return + /// Dictionary to search + /// Collection of Keys to find. + /// + public static string FirstNotNullOrEmpty(this IDictionary dictionary, params TKey[] keys) + { + return ( + from key in keys + where dictionary.ContainsKey(key) && !string.IsNullOrEmpty(dictionary[key]) + select dictionary[key]).FirstOrDefault(); + } + + } } diff --git a/src/GeneralTools/DataverseClient/Client/Utils/MSALLoggerCallBack.cs b/src/GeneralTools/DataverseClient/Client/Utils/MSALLoggerCallBack.cs new file mode 100644 index 0000000..e27b6b4 --- /dev/null +++ b/src/GeneralTools/DataverseClient/Client/Utils/MSALLoggerCallBack.cs @@ -0,0 +1,66 @@ +using Microsoft.Identity.Client; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.PowerPlatform.Dataverse.Client.Model; + +namespace Microsoft.PowerPlatform.Dataverse.Client.Utils +{ + /// + /// This class will be used to support hooking into MSAL Call back logic. + /// + internal static class MSALLoggerCallBack + { + private static DataverseTraceLogger _logEntry; + + /// + /// Enabled PII logging for this connection. + /// if this flag is set, it will override the value from app config. + /// + public static bool? EnabledPIILogging { get; set; } = null; + + /// + /// + /// + /// + /// + /// + static public void Log(LogLevel level, string message, bool containsPii) + { + if (_logEntry == null) + _logEntry = new DataverseTraceLogger("Microsoft.IdentityModel.Clients.ActiveDirectory"); // set up logging client. + + if (!EnabledPIILogging.HasValue) + { + EnabledPIILogging = ClientServiceProviders.Instance.GetService>().Value.MSALEnabledLogPII; + _logEntry.Log($"Setting MSAL PII Logging Feature to {EnabledPIILogging.Value}", System.Diagnostics.TraceEventType.Information); + } + + if (containsPii && !EnabledPIILogging.Value) + { + return; + } + + // Add (PII) prefix to messages that have PII in them per AAD Message alert. + message = containsPii ? $"(PII){message}" : message; + + switch (level) + { + case LogLevel.Info: + _logEntry.Log(message, System.Diagnostics.TraceEventType.Information); + break; + case LogLevel.Verbose: + _logEntry.Log(message, System.Diagnostics.TraceEventType.Verbose); + break; + case LogLevel.Warning: + _logEntry.Log(message, System.Diagnostics.TraceEventType.Warning); + break; + case LogLevel.Error: + _logEntry.Log(message, System.Diagnostics.TraceEventType.Error); + break; + default: + break; + } + } + + } +} diff --git a/src/GeneralTools/DataverseClient/Client/Utils/ServiceProviders.cs b/src/GeneralTools/DataverseClient/Client/Utils/ServiceProviders.cs index c5cecb8..4606240 100644 --- a/src/GeneralTools/DataverseClient/Client/Utils/ServiceProviders.cs +++ b/src/GeneralTools/DataverseClient/Client/Utils/ServiceProviders.cs @@ -1,10 +1,9 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.PowerPlatform.Dataverse.Client.Model; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Microsoft.PowerPlatform.Dataverse.Client.Auth; +using Microsoft.Extensions.Options; +using System.Net.Http; namespace Microsoft.PowerPlatform.Dataverse.Client.Utils { @@ -13,10 +12,10 @@ internal static class ClientServiceProviders /// /// Private property accessor for service provider /// - private static IServiceProvider _instance = null; + private static IServiceProvider _instance = null; /// - /// Instance of Service providers. + /// Instance of Service providers. /// internal static IServiceProvider Instance { @@ -30,15 +29,29 @@ internal static IServiceProvider Instance } } - private static void BindServiceProviders () + private static void BindServiceProviders() { if (_instance == null) { var services = new ServiceCollection(); + services.AddTransient(); services.AddOptions(); - services.AddHttpClient("DataverseHttpClientFactory"); - + services.AddHttpClient("DataverseHttpClientFactory") + .ConfigurePrimaryHttpMessageHandler(() => + { + var hander = new HttpClientHandler + { + UseCookies = false, + //SslProtocols = System.Security.Authentication.SslProtocols.Tls12 + }; + return hander; + }); + services.AddHttpClient("MSALClientFactory", (sp, client) => + { + client.Timeout = sp.GetService>().Value.MSALRequestTimeout; + }) + .AddHttpMessageHandler(); // Adding on board retry hander for MSAL. _instance = services.BuildServiceProvider(); } } diff --git a/src/GeneralTools/DataverseClient/Client/Utils/Utils.cs b/src/GeneralTools/DataverseClient/Client/Utils/Utils.cs index 6207a94..f929b7d 100644 --- a/src/GeneralTools/DataverseClient/Client/Utils/Utils.cs +++ b/src/GeneralTools/DataverseClient/Client/Utils/Utils.cs @@ -1,14 +1,18 @@ #region using +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Microsoft.PowerPlatform.Dataverse.Client.Model; using Microsoft.PowerPlatform.Dataverse.Client.Utils; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Discovery; using Microsoft.Xrm.Sdk.Metadata; using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Dynamic; using System.Linq; +using System.Net; using System.Net.Http; using System.Reflection; using System.Text; @@ -86,7 +90,7 @@ internal static DiscoveryServer GetDiscoveryServerByUri(Uri orgUri) /// /// Service Uri to parse /// if OnPrem, will be set to true, else false. - /// Name of the CRM on line Region serving this request + /// Name of the Dataverse Online Region serving this request /// Name of the Organization extracted from the Service URI public static void GetOrgnameAndOnlineRegionFromServiceUri(Uri serviceUri, out string onlineRegion, out string organizationName, out bool isOnPrem) { @@ -105,7 +109,7 @@ public static void GetOrgnameAndOnlineRegionFromServiceUri(Uri serviceUri, out s elements.RemoveAt(0); // remove the first ( org name ) from the Uri. - // construct Prospective CRM Online path. + // construct Prospective Dataverse Online path. System.Text.StringBuilder buildPath = new System.Text.StringBuilder(); foreach (var item in elements) { @@ -113,14 +117,14 @@ public static void GetOrgnameAndOnlineRegionFromServiceUri(Uri serviceUri, out s continue; // Skip the .api. when running via this path. buildPath.AppendFormat("{0}.", item); } - string crmKey = buildPath.ToString().TrimEnd('.').TrimEnd('/'); + string dvKey = buildPath.ToString().TrimEnd('.').TrimEnd('/'); buildPath.Clear(); - if (!string.IsNullOrEmpty(crmKey)) + if (!string.IsNullOrEmpty(dvKey)) { using (DiscoveryServers discoSvcs = new DiscoveryServers()) { // drop in the discovery region if it can be determined. if not, default to scanning. - var locatedDiscoServer = discoSvcs.OSDPServers.Where(w => w.DiscoveryServerUri != null && w.DiscoveryServerUri.Host.Contains(crmKey)).FirstOrDefault(); + var locatedDiscoServer = discoSvcs.OSDPServers.Where(w => w.DiscoveryServerUri != null && w.DiscoveryServerUri.Host.Contains(dvKey)).FirstOrDefault(); if (locatedDiscoServer != null && !string.IsNullOrEmpty(locatedDiscoServer.ShortName)) onlineRegion = locatedDiscoServer.ShortName; } @@ -228,14 +232,14 @@ public static DiscoveryServer DeterminDiscoveryDataFromOrgDetail(Uri serviceUri, continue; // Skip the .api. when running via this path. buildPath.AppendFormat("{0}.", item); } - string crmKey = buildPath.ToString().TrimEnd('.').TrimEnd('/'); + string dvKey = buildPath.ToString().TrimEnd('.').TrimEnd('/'); buildPath.Clear(); - if (!string.IsNullOrEmpty(crmKey)) + if (!string.IsNullOrEmpty(dvKey)) { using (DiscoveryServers discoSvcs = new DiscoveryServers()) { // drop in the discovery region if it can be determined. if not, default to scanning. - var locatedDiscoServer = discoSvcs.OSDPServers.Where(w => w.DiscoveryServerUri != null && w.DiscoveryServerUri.Host.Contains(crmKey)).FirstOrDefault(); + var locatedDiscoServer = discoSvcs.OSDPServers.Where(w => w.DiscoveryServerUri != null && w.DiscoveryServerUri.Host.Contains(dvKey)).FirstOrDefault(); if (locatedDiscoServer != null && !string.IsNullOrEmpty(locatedDiscoServer.ShortName)) return locatedDiscoServer; } @@ -291,24 +295,25 @@ public static bool IsValidOnlineHost(Uri hostUri) /// This is a temp method to support the staged transition to the webAPI and will be removed or reintegrated with the overall pipeline at some point in the future. /// /// - /// useWebApi /// - internal static bool IsRequestValidForTranslationToWebAPI(OrganizationRequest req, bool useWebApi) + internal static bool IsRequestValidForTranslationToWebAPI(OrganizationRequest req) { + bool useWebApi = ClientServiceProviders.Instance.GetService>().Value.UseWebApi; + bool useWebApiForLogin = ClientServiceProviders.Instance.GetService>().Value.UseWebApiLoginFlow; switch (req.RequestName.ToLowerInvariant()) { case "create": case "update": case "delete": - return true; - case "retrievecurrentorganization": - case "retrieveorganizationinfo": - case "retrieveversion": - case "whoami": case "importsolution": case "exportsolution": case "stagesolution": return useWebApi; // Only supported with useWebApi flag + case "retrievecurrentorganization": + case "retrieveorganizationinfo": + case "retrieveversion": + case "whoami": + return useWebApiForLogin; // Separate webAPI login methods from general WebAPI use. case "upsert": // Disabling WebAPI support for upsert right now due to issues with generating the response. @@ -357,7 +362,7 @@ internal static HttpMethod RequestNameToHttpVerb(string requestName) } /// - /// Constructs Web API request url and adds public request properties to the url as key/value pairs + /// Constructs Web API request url and adds public request properties to the url as key/value pairs /// internal static string ConstructWebApiRequestUrl(OrganizationRequest request, HttpMethod httpMethod, Entity entity, EntityMetadata entityMetadata) { @@ -443,7 +448,7 @@ internal static string ConstructWebApiRequestUrl(OrganizationRequest request, Ht /// retryCount /// when set indicated this was caused by a Throttle /// - internal static void RetryRequest(OrganizationRequest req, Guid requestTrackingId, TimeSpan LockWait, Stopwatch logDt, + internal static void RetryRequest(OrganizationRequest req, Guid requestTrackingId, TimeSpan LockWait, Stopwatch logDt, DataverseTraceLogger logEntry, Guid? sessionTrackingId, bool disableConnectionLocking, TimeSpan retryPauseTimeRunning, Exception ex, string errorStringCheck, ref int retryCount, bool isThrottled, string webUriReq = "") { @@ -457,9 +462,11 @@ internal static void RetryRequest(OrganizationRequest req, Guid requestTrackingI /// Parses an attribute array into a object that can be used to create a JSON request. /// /// Entity to process - /// - /// - internal static ExpandoObject ToExpandoObject(Entity sourceEntity, MetadataUtility mUtil) + /// Metadata interface utility + /// Operation being executed + /// Log sink + /// ExpandoObject + internal static ExpandoObject ToExpandoObject(Entity sourceEntity, MetadataUtility mUtil, HttpMethod requestedMethod, DataverseTraceLogger logger ) { dynamic expando = new ExpandoObject(); @@ -484,19 +491,32 @@ internal static ExpandoObject ToExpandoObject(Entity sourceEntity, MetadataUtili var keyValuePair = attrib; var value = keyValuePair.Value; var key = keyValuePair.Key; + if (value is EntityReference entityReference) { var attributeInfo = mUtil.GetAttributeMetadata(sourceEntity.LogicalName, key.ToLower()); + + if (!IsAttributeValidForOperation(attributeInfo, requestedMethod)) + continue; + // Get Lookup attribute meta data for the ER to check for polymorphic relationship. - if (attributeInfo is Xrm.Sdk.Metadata.LookupAttributeMetadata attribData) + if (attributeInfo is LookupAttributeMetadata attribData) { // Now get relationship to make sure we use the correct name. - var eData = mUtil.GetEntityMetadata(Xrm.Sdk.Metadata.EntityFilters.Relationships, sourceEntity.LogicalName); + var eData = mUtil.GetEntityMetadata(EntityFilters.Relationships, sourceEntity.LogicalName); var ERNavName = eData.ManyToOneRelationships.FirstOrDefault(w => w.ReferencingAttribute.Equals(attribData.LogicalName) && w.ReferencedEntity.Equals(entityReference.LogicalName)) ?.ReferencingEntityNavigationPropertyName; + if (!string.IsNullOrEmpty(ERNavName)) + { key = ERNavName; + } + else + { + logger.Log($"{key} describes an entity reference but does not have a corresponding relationship. Skipping adding it in the {requestedMethod} operation"); + continue; + } // Populate Key property key = $"{key}@odata.bind"; @@ -519,7 +539,7 @@ internal static ExpandoObject ToExpandoObject(Entity sourceEntity, MetadataUtili } - value = $"/{mUtil.GetEntityMetadata(Xrm.Sdk.Metadata.EntityFilters.Entity, entityReference.LogicalName).EntitySetName}({entityReferanceValue})"; + value = $"/{mUtil.GetEntityMetadata(EntityFilters.Entity, entityReference.LogicalName).EntitySetName}({entityReferanceValue})"; } else { @@ -541,7 +561,7 @@ internal static ExpandoObject ToExpandoObject(Entity sourceEntity, MetadataUtili // build linked collection here. foreach (var ent in (value as EntityCollection).Entities) { - ExpandoObject rslt = ToExpandoObject(ent, mUtil); + ExpandoObject rslt = ToExpandoObject(ent, mUtil, requestedMethod , logger); if (isActivityParty) { var tempDict = ((IDictionary)rslt); @@ -563,7 +583,7 @@ internal static ExpandoObject ToExpandoObject(Entity sourceEntity, MetadataUtili string mselectValueString = string.Empty; foreach (var opt in optionSetValues) { - mselectValueString += $"{opt.Value.ToString()},"; + mselectValueString += $"{opt.Value},"; } value = mselectValueString.Remove(mselectValueString.Length - 1); } @@ -574,21 +594,21 @@ internal static ExpandoObject ToExpandoObject(Entity sourceEntity, MetadataUtili else if (value is DateTime dateTimeValue) { var attributeInfo = mUtil.GetAttributeMetadata(sourceEntity.LogicalName, key.ToLower()); - if (attributeInfo is Xrm.Sdk.Metadata.DateTimeAttributeMetadata attribDateTimeData) + if (attributeInfo is DateTimeAttributeMetadata attribDateTimeData) { - if (attribDateTimeData.DateTimeBehavior == Xrm.Sdk.Metadata.DateTimeBehavior.DateOnly) + if (attribDateTimeData.DateTimeBehavior == DateTimeBehavior.DateOnly) { value = dateTimeValue.ToUniversalTime().ToString("yyyy-MM-dd"); } else { - if (attribDateTimeData.DateTimeBehavior == Xrm.Sdk.Metadata.DateTimeBehavior.TimeZoneIndependent) + if (attribDateTimeData.DateTimeBehavior == DateTimeBehavior.TimeZoneIndependent) { value = dateTimeValue.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fff"); } else { - if (attribDateTimeData.DateTimeBehavior == Xrm.Sdk.Metadata.DateTimeBehavior.UserLocal) + if (attribDateTimeData.DateTimeBehavior == DateTimeBehavior.UserLocal) { value = dateTimeValue.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); } @@ -611,10 +631,14 @@ internal static ExpandoObject ToExpandoObject(Entity sourceEntity, MetadataUtili else if (value is null) { var attributeInfo = mUtil.GetAttributeMetadata(sourceEntity.LogicalName, key.ToLower()); + + if (!IsAttributeValidForOperation((AttributeMetadata)attrib.Value, requestedMethod)) + continue; + if (attributeInfo is Xrm.Sdk.Metadata.LookupAttributeMetadata attribData) { // This will not work for Polymorphic currently. - var eData = mUtil.GetEntityMetadata(Xrm.Sdk.Metadata.EntityFilters.Relationships, sourceEntity.LogicalName); + var eData = mUtil.GetEntityMetadata(EntityFilters.Relationships, sourceEntity.LogicalName); var ERNavName = eData.ManyToOneRelationships.FirstOrDefault(w => w.ReferencingAttribute.Equals(attribData.LogicalName) && w.ReferencedEntity.Equals(attribData.Targets.FirstOrDefault())) ?.ReferencingEntityNavigationPropertyName; @@ -634,13 +658,40 @@ internal static ExpandoObject ToExpandoObject(Entity sourceEntity, MetadataUtili // Check to see if this contained an activity party if (partiesCollection?.Count > 0) { - expandoObject.Add($"{sourceEntity.LogicalName}_activity_parties", partiesCollection); + var sourceMdata = mUtil.GetEntityMetadata(sourceEntity.LogicalName); + if (sourceMdata != null ) + expandoObject.Add($"{sourceMdata.SchemaName}_activity_parties", partiesCollection); } return (ExpandoObject)expandoObject; } + /// + /// Checks if the operation being preformed is permitted for the attribute. + /// + /// + /// + /// + private static bool IsAttributeValidForOperation(AttributeMetadata attrib, HttpMethod requestedMethod) + { + switch (requestedMethod.ToString().ToLowerInvariant()) + { + case "post": + case "put": + if (attrib.IsValidForCreate.HasValue && !attrib.IsValidForCreate.Value) + return false; + break; + case "patch": + if (attrib.IsValidForUpdate.HasValue && !attrib.IsValidForUpdate.Value) + return false; + break; + default: + break; + } + return true; + } + /// /// checks to see if an attribute has been added to the collection containing the ID of the entity . /// this is required for the WebAPI to properly function. @@ -665,8 +716,10 @@ private static Entity UpdateEntityAttributesForPrimaryId(Entity sourceEntity, Me /// parent entity /// collection of relationships /// meta-data utility + /// Operation being executed + /// Logger /// - internal static ExpandoObject ReleatedEntitiesToExpandoObject(ExpandoObject rootExpando, string entityName, RelatedEntityCollection entityCollection, MetadataUtility mUtil) + internal static ExpandoObject ReleatedEntitiesToExpandoObject(ExpandoObject rootExpando, string entityName, RelatedEntityCollection entityCollection, MetadataUtility mUtil, HttpMethod requestedMethod, DataverseTraceLogger logger) { if (rootExpando == null) return rootExpando; @@ -688,7 +741,7 @@ internal static ExpandoObject ReleatedEntitiesToExpandoObject(ExpandoObject root List childCollection = new List(); // Get the Entity relationship key and entity and reverse it back to the entity key name - var eData = mUtil.GetEntityMetadata(Xrm.Sdk.Metadata.EntityFilters.Relationships, entItem.Value.Entities[0].LogicalName); + var eData = mUtil.GetEntityMetadata(EntityFilters.Relationships, entItem.Value.Entities[0].LogicalName); // Find the relationship that is referenced. var ERM21 = eData.ManyToOneRelationships.FirstOrDefault(w1 => w1.SchemaName.ToLower().Equals(entItem.Key.SchemaName.ToLower())); @@ -728,20 +781,20 @@ internal static ExpandoObject ReleatedEntitiesToExpandoObject(ExpandoObject root // Check to see if the entity itself has related entities if (ent.RelatedEntities != null && ent.RelatedEntities.Count > 0) { - childEntities = ReleatedEntitiesToExpandoObject(childEntities, entityName, ent.RelatedEntities, mUtil); + childEntities = ReleatedEntitiesToExpandoObject(childEntities, entityName, ent.RelatedEntities, mUtil, requestedMethod, logger); } // generate object. - ExpandoObject ent1 = ToExpandoObject(ent, mUtil); + ExpandoObject ent1 = ToExpandoObject(ent, mUtil, requestedMethod , logger); if (((IDictionary)childEntities).Count() > 0) { - foreach (var item in (IDictionary)childEntities) + foreach (var item in childEntities) { ((IDictionary)ent1).Add(item.Key, item.Value); } } - childCollection?.Add((ExpandoObject)ent1); + childCollection?.Add(ent1); } if (childCollection.Count == 1 && isArrayRequired == false) ((IDictionary)rootExpando).Add(key, childCollection[0]); @@ -786,9 +839,11 @@ internal static bool ShouldAutoRetryRetrieveByEntityName(string queryStringToPar { if (_autoRetryRetrieveEntityList == null) { - _autoRetryRetrieveEntityList = new List(); - _autoRetryRetrieveEntityList.Add("asyncoperation"); // to support failures when looking for async Jobs. - _autoRetryRetrieveEntityList.Add("importjob"); // to support failures when looking for importjob. + _autoRetryRetrieveEntityList = new List + { + "asyncoperation", // to support failures when looking for async Jobs. + "importjob" // to support failures when looking for importjob. + }; } foreach (var itm in _autoRetryRetrieveEntityList) @@ -826,39 +881,39 @@ internal static class RequestHeaders /// /// Populated with the host process /// - public static readonly string USER_AGENT_HTTP_HEADER = "User-Agent"; + public const string USER_AGENT_HTTP_HEADER = "User-Agent"; /// /// Session ID used to track all operations associated with a given group of calls. /// - public static readonly string X_MS_CLIENT_SESSION_ID = "x-ms-client-session-id"; + public const string X_MS_CLIENT_SESSION_ID = "x-ms-client-session-id"; /// /// PerRequest ID used to track a specific request. /// - public static readonly string X_MS_CLIENT_REQUEST_ID = "x-ms-client-request-id"; + public const string X_MS_CLIENT_REQUEST_ID = "x-ms-client-request-id"; /// /// Content type of WebAPI request. /// - public static readonly string CONTENT_TYPE = "Content-Type"; + public const string CONTENT_TYPE = "Content-Type"; /// /// Header loaded with the AADObjectID of the user to impersonate /// - public static readonly string AAD_CALLER_OBJECT_ID_HTTP_HEADER = "CallerObjectId"; + public const string AAD_CALLER_OBJECT_ID_HTTP_HEADER = "CallerObjectId"; /// - /// Header loaded with the CRM user ID of the user to impersonate + /// Header loaded with the Dataverse user ID of the user to impersonate /// - public static readonly string CALLER_OBJECT_ID_HTTP_HEADER = "MSCRMCallerID"; + public const string CALLER_OBJECT_ID_HTTP_HEADER = "MSCRMCallerID"; /// /// Header used to pass the token for the user /// - public static readonly string AUTHORIZATION_HEADER = "Authorization"; + public const string AUTHORIZATION_HEADER = "Authorization"; /// /// Header requesting the connection be kept alive. /// - public static readonly string CONNECTION_KEEP_ALIVE = "Keep-Alive"; + public const string CONNECTION_KEEP_ALIVE = "Keep-Alive"; /// /// Header requiring Cache Consistency Server side. /// - public static readonly string FORCE_CONSISTENCY = "Consistency"; + public const string FORCE_CONSISTENCY = "Consistency"; /// /// This key used to indicate if the custom plugins need to be bypassed during the execution of the request. @@ -983,5 +1038,157 @@ internal static bool IsFeatureValidForEnviroment(Version instanceVersion, Versio } + #region CookieHelpers + /// + /// Manage Pushing Cookies Forward in a switchable manner. + /// + /// Header string to start with + /// Collection of cookies currently in the system + /// + internal static Dictionary GetAllCookiesFromHeader(string strHeader, Dictionary cookieCollection) + { + ArrayList al = ConvertCookieHeaderToArrayList(strHeader); + return ConvertCookieArraysToCookieDictionary(al, cookieCollection); + } + + /// + /// Manage Pushing Cookies Forward in a switchable manner. + /// + /// Header string to start with + /// collection of cookies currently in the system + /// + internal static Dictionary GetAllCookiesFromHeader(string[] strHeaderList, Dictionary cookieCollection) + { + if (strHeaderList != null) + { + return ConvertCookieArraysToCookieDictionary(ConvertCookieListToArrayList(strHeaderList), cookieCollection); + } + else + { + return cookieCollection; + } + } + + internal static string GetCookiesFromCollectionAsString(Dictionary cookieCollection) + { + if (cookieCollection == null || cookieCollection.Count == 0) + return string.Empty; + + string cookieString = ""; + if (cookieCollection != null) + { + foreach (var itm in cookieCollection) + { + cookieString += $"{itm.Key}={itm.Value};"; + } + } + return cookieString; + } + + internal static List GetCookiesFromCollectionAsArray(Dictionary cookieCollection) + { + if (cookieCollection == null || cookieCollection.Count == 0) + return null; + + string s = ""; + List cookieItems = new List(); + foreach (var itm in cookieCollection) + { + //cookieItems.Add($"{itm.Key}={itm.Value}; "); + s += $"{itm.Key}={itm.Value}; "; + } + cookieItems.Add(s); + return cookieItems; + } + + /// + /// Create an array list of cookies + /// + /// + /// + private static ArrayList ConvertCookieHeaderToArrayList(string strCookHeader) + { + if (string.IsNullOrEmpty(strCookHeader)) + return null; + + strCookHeader = strCookHeader.Replace("\r", ""); + strCookHeader = strCookHeader.Replace("\n", ""); + string[] strCookTemp = strCookHeader.Split(','); + + return ConvertCookieListToArrayList(strCookTemp); + + } + + private static ArrayList ConvertCookieListToArrayList(string[] potentalCookieList) + { + ArrayList al = new ArrayList(); + int i = 0; + int n = potentalCookieList.Length; + try + { + while (i < n) + { + if (potentalCookieList[i].IndexOf("expires=", StringComparison.OrdinalIgnoreCase) > 0) + { + if (n == (i + 1)) + { + al.Add(potentalCookieList[i]); + } + else + { + al.Add(potentalCookieList[i] + "," + potentalCookieList[i + 1]); + } + i++; + } + else + { + al.Add(potentalCookieList[i]); + } + i++; + } + } + finally { } // No op.. let it fail if it could not parse the cookie. + return al; + } + + /// + /// Generate Cookie collection for the Array. + /// + /// + /// Cookie collection to populate or update + /// + private static Dictionary ConvertCookieArraysToCookieDictionary(ArrayList al, Dictionary cookieCollection) + { + if (cookieCollection == null) + cookieCollection = new Dictionary(); + + int alcount = al.Count; + string strEachCook; + string[] strEachCookParts; + for (int i = 0; i < alcount; i++) + { + strEachCook = al[i].ToString(); + strEachCookParts = strEachCook.Split(';'); + int intEachCookPartsCount = strEachCookParts.Length; + Cookie cookTemp = new Cookie(); + + string strCNameAndCValue = strEachCookParts[0]; + if (!string.IsNullOrEmpty(strCNameAndCValue)) + { + int firstEqual = strCNameAndCValue.IndexOf("="); + string firstName = strCNameAndCValue.Substring(0, firstEqual); + string allValue = strCNameAndCValue.Substring(firstEqual + 1, strCNameAndCValue.Length - (firstEqual + 1)); + cookTemp.Name = firstName; + cookTemp.Value = allValue; + if (cookieCollection.ContainsKey(firstName)) + cookieCollection[firstName] = allValue; + else + cookieCollection.Add(firstName, allValue); + } + } + return cookieCollection; + } + #endregion + } } diff --git a/src/GeneralTools/DataverseClient/ConnectControl/ConnectionManager.cs b/src/GeneralTools/DataverseClient/ConnectControl/ConnectionManager.cs new file mode 100644 index 0000000..f2fdae1 --- /dev/null +++ b/src/GeneralTools/DataverseClient/ConnectControl/ConnectionManager.cs @@ -0,0 +1,2358 @@ +using Microsoft.Identity.Client; +using Microsoft.IdentityModel.Clients.ActiveDirectory; +using Microsoft.PowerPlatform.Dataverse.Client; +using Microsoft.PowerPlatform.Dataverse.Client.Model; +using Microsoft.PowerPlatform.Dataverse.ConnectControl.Model; +using Microsoft.PowerPlatform.Dataverse.ConnectControl.Properties; +using Microsoft.PowerPlatform.Dataverse.ConnectControl.Utility; +using Microsoft.Xrm.Sdk.Discovery; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Configuration; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Security; +using System.ServiceModel.Description; +using System.ServiceModel.Security; +using System.Text; +using System.Web.Services.Protocols; +using System.Windows.Controls; +using System.Windows.Threading; + +namespace Microsoft.PowerPlatform.Dataverse.ConnectControl +{ + /// + /// Provides Connection logic and error handling for connecting to CRM2011. This class is designed to operate in the background, off the primary user UI thread. + /// This class raises events that can be used to update the user with progress reports. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")] + public class ConnectionManager : IDisposable + { + #region vars + /// + /// Background worker process for loading Connection To the server. + /// + private BackgroundWorker _bgWorker = null; + + /// + /// Keys for the Server Configuration Info. + /// + private Dictionary ServerConfigKeys; + + /// + /// Dataverse Connection Object. + /// + public ServiceClient ServiceClient { get; set; } + + /// + /// Login Tracing System + /// + private LoginTracer _tracer = new LoginTracer(); + + /// + /// contains the current deployment type. + /// + private CrmDeploymentType _deploymentType = CrmDeploymentType.O365; + + /// + /// determines if SSL Is required for Login + /// + private bool IsSSLReq = false; + + /// + /// determines if the current Authentication type is OAuth + /// + private bool IsOAuth = false; + + /// + /// determines if the Default Credentials can be used + /// + private bool UseDefaultCreds = false; + + /// + /// determines if Advanced Check box is enabled + /// + private bool IsAdvancedCheckEnabled = false; + + /// + /// Prompt Behavior to work on + /// + private Client.Auth.PromptBehavior _promptBehavior = Client.Auth.PromptBehavior.Auto; + + /// + /// Home Realm of the user. if Present, it is provided for Claims Auth + /// + private Uri uUserHomeRealm = null; + + /// + /// Use Creds for Login to CRM for Prem Solutions + /// + private NetworkCredential _userCred; + + /// + /// Describes the user Client Credential + /// + private ClientCredentials _userClientCred; + + /// + /// Device Credentials for Connecting to + /// + private ClientCredentials DeviceCredentials; + + /// + /// Org List View that will be assigned to the Select Org Dialog box. + /// this is created here to limit the impact on user XAML code. + /// + private CrmOrgList _orgListView = new CrmOrgList(); + + /// + /// Private variable that contains the CRM Discovery Server List. + /// + private OnlineDiscoveryServers _onlineDiscoveryServerList = null; + + /// + /// Private variable that contains the HomeRealmList + /// + private ClaimsHomeRealmOptions _homeRealmServersList = null; + + /// + /// Profile name to use for this connection, only valid if UseUserLocalDirectory is set to true + /// + private string _profileName; + + /// + /// flag indicating that the last error was sent though a status update. + /// + private bool _lastErrorSent = false; + + /// + /// Private variable that contains cached authority (to store in) \(fetch from) credential managaer + /// + private string _cachedAuthorityName = null; + + /// + /// Private variable that contains cached userId (to store in) \(fetch from) config file + /// + private string _cachedUserId = null; + + /// + /// if true, Skip discovery is in focus + /// + private bool _isSkipDiscovery = false; + + /// + /// if populated, contains the URL to try a direct connect too. + /// + private string _directConnectUri = string.Empty; + + /// + /// format string for the global discovery service + /// + private static readonly string _globalDiscoBaseWebAPIUriFormat = "https://globaldisco.crm.dynamics.com/api/discovery/v{0}/{1}"; + + /// + /// version of the globaldiscovery service. + /// + private static readonly string _globlaDiscoVersion = "2.0"; + + #endregion + + #region Events + + /// + /// Raised when a status is updated + /// + public event EventHandler ServerConnectionStatusUpdate; + /// + /// Raised when the connection process completes + /// + public event EventHandler ConnectionCheckComplete; + + #endregion + + #region Properties + + /// + /// Collection of CRM Discovery Servers + /// if a server list has not been submitted to this property, and get is called, a new instance of the OnlineDiscoveryServers is created. + /// + public OnlineDiscoveryServers OnlineDiscoveryServerList { get { if (_onlineDiscoveryServerList == null) _onlineDiscoveryServerList = new OnlineDiscoveryServers(); return _onlineDiscoveryServerList; } set { _onlineDiscoveryServerList = value; } } + + /// + /// Collection of HomeRealms Loaded by Configuration + /// + public ClaimsHomeRealmOptions HomeRealmServersList { get { if (_homeRealmServersList == null) _homeRealmServersList = new ClaimsHomeRealmOptions(); return _homeRealmServersList; } set { _homeRealmServersList = value; } } + + /// + /// User Identifier as a login hint + /// + public UserIdentifier UserId { get; set; } + + /// + /// ClientId for the client + /// + public string ClientId { get; set; } + + /// + /// Resource for the resource + /// + public Uri RedirectUri { get; set; } + + /// + /// Token Cache Path where tokencache file will be stored. + /// + public string TokenCachePath { get; set; } + + /// + /// List of Organizations that have been requested by the user. + /// + public CrmOrgList CrmOrgsFoundForUser { get { return _orgListView; } } + + /// + /// This the parent control that invoked me. + /// + public UserControl ParentControl { get; set; } + + /// + /// Returns the friendly name of the connected org. + /// + public string ConnectedOrgFriendlyName { get; private set; } + /// + /// Returns the unique name for the org that has been connected. + /// + public string ConnectedOrgUniqueName { get; private set; } + /// + /// Returns the endpoint collection for the connected org. + /// + public EndpointCollection ConnectedOrgPublishedEndpoints { get; private set; } + + /// + /// Returns the unique Id of the connected organization. + /// + public Guid ConnectedOrgId { get; private set; } + + /// + /// Tells the system to store the user config in the users local app directory instead of the Exe directory. + /// + public bool UseUserLocalDirectoryForConfigStore { get; set; } + /// + /// Used in conjunction with the UseUserLocalDirecotryForConfigStore, Allows you to set a name for the config application to use. + /// This is used when the host application cannot provide a proper AppDomain.Current.FriendlyName + /// + public string HostApplicatioNameOveride { get; set; } + + /// + /// Returns Last successful DeploymentType. + /// + internal string LastDeploymentType { get; set; } + + /// + /// Profile name to use for this login process. + /// + public string ProfileName + { + get + { + return _profileName; + } + set + { + // Check for validSet + bool bFail = false; + if (value.IndexOfAny(Path.GetInvalidFileNameChars()) != -1) + bFail = true; + if (value.IndexOfAny(Path.GetInvalidPathChars()) != -1) + bFail = true; + if (bFail) + throw new System.ArgumentOutOfRangeException(Messages.CRMCONNECT_PROFILENAME_INVALID); + _profileName = value; + } + } + + /// + /// last error from the connection manager + /// + public string LastError { get { return _tracer != null ? _tracer.LastError : string.Empty; } } + + /// + /// Last Exception from the connection manager. + /// + public Exception LastException { get { return _tracer != null ? _tracer.LastException : null; } } + + /// + /// Forces an OAuth Prompt on the first request for this connection. + /// + public bool ForceFirstOAuthPrompt { get; set; } + + /// + /// returns the URL to global discovery for querying all instances. + /// + private string GlobalDiscoveryAllInstancesUri { get { return string.Format(_globalDiscoBaseWebAPIUriFormat, _globlaDiscoVersion, "Instances"); } } + /// + /// Format string for calling global disco for a specific instance. + /// + private string GlobalDiscoveryInstanceUriFormat { get { return string.Format(_globalDiscoBaseWebAPIUriFormat, _globlaDiscoVersion, "Instances({0})"); } } + + #endregion + + /// + /// Default constructor. + /// + public ConnectionManager() + { + // Default Profile setting + ProfileName = "Default"; + + // Configure the background worker. + InitBackgroundWorker(); + _tracer.Log("Created CrmConnectionManager ", TraceEventType.Information); + } + + /// + /// Clean up background worker on exit. + /// + ~ConnectionManager() + { + if (_bgWorker != null) + _bgWorker.Dispose(); + } + + /// + /// Begins the authentication process for CRM. + /// + public void ConnectToServerCheck() + { + _tracer.Log("ConnectToServerCheck()", TraceEventType.Start); + + if (_bgWorker != null) + _bgWorker.RunWorkerAsync(new object()); + + _tracer.Log("ConnectToServerCheck()", TraceEventType.Stop); + } + + /// + /// Begin Connect check to server + /// + /// + public void ConnectToServerCheck(OrgByServer selectedOrgToConnectTo) + { + _tracer.Log("ConnectToServerCheck( OrganizationDetail selectedOrgToConnectTo)", TraceEventType.Start); + + if (_bgWorker != null) + _bgWorker.RunWorkerAsync(selectedOrgToConnectTo); + + _tracer.Log("ConnectToServerCheck( OrganizationDetail selectedOrgToConnectTo)", TraceEventType.Stop); + } + + /// + /// Cancel Connection process. + /// + public void CancelConnectToServerCheck() + { + _tracer.Log("CancelConnectToServerCheck()", TraceEventType.Start); + if (_bgWorker != null) + _bgWorker.CancelAsync(); + + _tracer.Log("CancelConnectToServerCheck()", TraceEventType.Stop); + } + + /// + /// This will check the configuration to determine if a user login is required. and if so, return true. + /// + /// + public bool RequireUserLogin() + { + _tracer.Log("RequireUserLogin()", TraceEventType.Start); + + if (ServerConfigKeys == null || ServerConfigKeys.Count <= 4) + LoadConfigFromFile(); + + // Default to Require login. + bool isUserLoginRequired = true; + + if (ValidateUserSpecifiedData()) + isUserLoginRequired = false; + + // Handle the switch being set in configuration, outside of the UI. + bool cacheCreds = string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CacheCredentials)) ? true : + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CacheCredentials).Equals(true.ToString(), StringComparison.CurrentCultureIgnoreCase); + + if (!cacheCreds) + isUserLoginRequired = true; + + _tracer.Log("RequireUserLogin()", TraceEventType.Stop); + // If the cache credentials is true, then we want to allow for a direct login, else we want to force a user login. + return isUserLoginRequired; + } + + /// + /// Sets the current connection information for the server. + /// this can be used to pass in a preconfigured list of keys + /// + /// + public void SetConfigKeyInformation(Dictionary configKeys) + { + _tracer.Log(string.Format("SetConfigKeyInfo, Key Count = {0}", configKeys.Count), TraceEventType.Information); + ServerConfigKeys = configKeys; + } + + #region Threaded handlers + + /// + /// Initializes the worker process + /// + /// + private void InitBackgroundWorker() + { + _tracer.Log("InitBackgroundWorker()", TraceEventType.Start); + _bgWorker = new BackgroundWorker(); + _bgWorker.WorkerReportsProgress = true; + _bgWorker.WorkerSupportsCancellation = true; + _bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork); + _bgWorker.ProgressChanged += new ProgressChangedEventHandler(bgWorker_ProgressChanged); + _bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted); + _tracer.Log("InitBackgroundWorker()", TraceEventType.Stop); + } + + /// + /// Raised when the connection is complete. + /// + /// + /// + private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + _tracer.Log("bgWorker_RunWorkerCompleted()", TraceEventType.Start); + if (e != null) + { + if (e.Result is bool) + { + if (ConnectionCheckComplete != null) + if (_orgListView != null && _orgListView.OrgsList != null && _orgListView.OrgsList.Count > 1) + ConnectionCheckComplete(this, new ServerConnectStatusEventArgs(string.Empty, (bool)e.Result, true)); + else + if ((bool)e.Result) + { + // Good Connect... + ConnectionCheckComplete(this, new ServerConnectStatusEventArgs(string.Empty, (bool)e.Result) + { + StatusMessage = (bool)e.Result ? Messages.CRMCONNECT_SERVER_CONNECT_GOOD : + ServiceClient != null ? ServiceClient.LastError : + string.Empty + }); + } + else + { + // Bad Connect + if (!_lastErrorSent) + { + if (ServerConnectionStatusUpdate != null) + { + ServerConnectionStatusUpdate(this, + new ServerConnectStatusEventArgs(string.Format(Messages.CRMCONNECT_LOGIN_UNABLE_TO_CONNECT_GENERAL, StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg), false, + new Exception( + ServiceClient != null && string.IsNullOrWhiteSpace(ServiceClient.LastError) ? ServiceClient.LastError : Messages.CRMCONNECT_LOGIN_UNABLE_TO_CONNECT_GENERAL + ))) + ); + } + } + + ConnectionCheckComplete(this, new ServerConnectStatusEventArgs(string.Empty, (bool)e.Result) + { + StatusMessage = (bool)e.Result ? Messages.CRMCONNECT_SERVER_CONNECT_GOOD : + ServiceClient != null ? ServiceClient.LastError : + "BadConnect" + }); + } + } + } + else + if (ConnectionCheckComplete != null) + ConnectionCheckComplete(this, new ServerConnectStatusEventArgs(string.Empty, false)); + + _tracer.Log("bgWorker_RunWorkerCompleted()", TraceEventType.Stop); + } + + /// + /// Raised for status events from the worker process + /// + /// + /// + private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + _lastErrorSent = false; + _tracer.Log("bgWorker_ProgressChanged()", TraceEventType.Start); + if (e != null) + { + if (e.UserState is ServerConnectStatusEventArgs) + { + if (e.ProgressPercentage == 100) + { + // Log last error here... + if (!string.IsNullOrWhiteSpace(((ServerConnectStatusEventArgs)e.UserState).StatusMessage) || + !string.IsNullOrWhiteSpace(((ServerConnectStatusEventArgs)e.UserState).ErrorMessage) || + ((ServerConnectStatusEventArgs)e.UserState).exEvent != null) + { + _lastErrorSent = true; + } + } + // in Connection Validation... + if (ServerConnectionStatusUpdate != null) + { + ServerConnectionStatusUpdate(this, (ServerConnectStatusEventArgs)e.UserState); + _tracer.Log(string.Format("Progress changed to: {0}", ((ServerConnectStatusEventArgs)e.UserState).StatusMessage), TraceEventType.Stop); + } + } + } + else + if (ServerConnectionStatusUpdate != null) + ServerConnectionStatusUpdate(this, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_ERR_UNEXPECTED_STATUS)); + _tracer.Log("bgWorker_ProgressChanged()", TraceEventType.Stop); + } + + /// + /// Handler that executes Background worker. + /// + /// + /// + private void bgWorker_DoWork(object sender, DoWorkEventArgs e) + { + _tracer.Log("bgWorker_DoWork()", TraceEventType.Start); + _bgWorker.ReportProgress(1, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_PROCESS_CONNECT_TO_UII)); + + // Run Connect + bool bServerGood = false; + if (e.Argument != null && e.Argument is OrgByServer) + bServerGood = ValidateServerConnection((OrgByServer)e.Argument); + else + bServerGood = ValidateServerConnection(null); + e.Result = bServerGood; + + if (_orgListView != null && _orgListView.OrgsList != null && _orgListView.OrgsList.Count > 1) + // Orgs found in the last run... Raise the MultiOrg Event. + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs("User attention required", false, true)); + + + if ((bool)e.Result) + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_SERVER_CONNECT_GOOD)); + + _tracer.Log("bgWorker_DoWork()", TraceEventType.Stop); + } + + #endregion + + + /// + /// Validate and connect to the server. + /// + /// User Selected Org. + /// + private bool ValidateServerConnection(OrgByServer selectedOrg) + { + _tracer.Log("ValidateServerConnection()", TraceEventType.Start); + + if (ServerConfigKeys == null || ServerConfigKeys.Count <= 4) + LoadConfigFromFile(); + + string sErrorMessage = string.Empty; + + // Clears out the connected org information. + ConnectedOrgFriendlyName = string.Empty; + // Clear out the OrgList + ClearOrgList(); + + //Check for and handle ForceOSDP flag in app.config of supporting program, mostly used for PD, DMT and other tools consuming tooling connector. + ResetOAuthIfOSDPOverrideSwitch(); + + // URI check.. + // Make sure there is a value + if (!ValidateUserSpecifiedData()) return false; + + // Check to see if its a direct connect. + bool.TryParse(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.UseDirectConnection), out _isSkipDiscovery); + if (_isSkipDiscovery) + _directConnectUri = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.DirectConnectionUri); + + // Value is not a bool.. check to see if there is a value. + string sDeploymentType = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDeploymentType); + if (string.IsNullOrWhiteSpace(sDeploymentType)) + _deploymentType = CrmDeploymentType.Prem; + else + { + // there is a value. + if (sDeploymentType.Equals(CrmDeploymentType.O365.ToString(), StringComparison.OrdinalIgnoreCase)) + _deploymentType = CrmDeploymentType.O365; + else + if (sDeploymentType.Equals(CrmDeploymentType.Online.ToString(), StringComparison.OrdinalIgnoreCase)) + _deploymentType = CrmDeploymentType.Online; + else + _deploymentType = CrmDeploymentType.Prem; + } + _tracer.Log($"Using CRM deployment type {_deploymentType}", TraceEventType.Information); + + if (!bool.TryParse(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUseSSL), out IsSSLReq)) + IsSSLReq = false; + + _tracer.Log($"SSL Connection = {IsSSLReq}", TraceEventType.Information); + + // Flush out ServiceClient Connection + ServiceClient = null; + + // Check for saving login creds: + if (RequireUserLogin()) + TokenCachePath = string.Empty; + + _bgWorker.ReportProgress(25, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_PROCESS_INIT_UII_CONNECTION)); + + try + { + uUserHomeRealm = null; // Reset HomeRealm Info + IsOAuth = false; // Reset OAuth Information + if (!bool.TryParse(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.UseDefaultCreds), out UseDefaultCreds)) + { + UseDefaultCreds = false; + } + + // Get Orgs and Direct ( single org ) login. + if (_deploymentType == CrmDeploymentType.Prem) + { + #region On-PremCode + // On Premise - Build Discovery Connection string. + + // You cannot use Default config for anything other then AD or IFD Setting's in the Auth Realm. + if ((StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.AuthHomeRealm).Equals(Resources.LOGIN_FRM_AUTHTYPE_AD) || + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.AuthHomeRealm).Equals(Resources.LOGIN_FRM_AUTHTYPE_IFD))) + { + + // Credentials support Default Credentials... + if (UseDefaultCreds) + // Use default creds.. + _userCred = System.Net.CredentialCache.DefaultNetworkCredentials; + else + { + // Build Creds + _userCred = + new System.Net.NetworkCredential( + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName), + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPassword), + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDomain) + ); + + if (!string.IsNullOrWhiteSpace(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDomain)) && + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.AuthHomeRealm).Equals(Resources.LOGIN_FRM_AUTHTYPE_IFD)) + { + // Domain is present... see if the user typed it into the user name box too... + if (!StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName).Contains( + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDomain))) + { + // they don't match.. prepend it.. + + string overrideUserName = string.Format("{0}\\{1}", + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDomain), + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName)); + + if (!string.IsNullOrWhiteSpace(overrideUserName)) + _userCred.UserName = overrideUserName; + } + } + } + } + else if (StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.AuthHomeRealm).Equals(Resources.LOGIN_FRM_AUTHTYPE_OAUTH)) + { + IsOAuth = true; + if (UseDefaultCreds) + // Use default creds.. + _userCred = System.Net.CredentialCache.DefaultNetworkCredentials; + else + { + // Build Creds + _userCred = + new System.Net.NetworkCredential( + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName), + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPassword), + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDomain) + ); + } + + if (_userClientCred == null) + _userClientCred = new ClientCredentials(); + + string overrideUserName = string.Empty; + if (!string.IsNullOrWhiteSpace(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDomain))) + { + // Domain is present... see if the user typed it into the user name box too... + if (!StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName).Contains( + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDomain))) + { + // they don't match.. prepend it.. + overrideUserName = string.Format("{0}\\{1}", + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDomain), + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName)); + + } + } + + if (string.IsNullOrWhiteSpace(overrideUserName)) + _userClientCred.UserName.UserName = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName); + else + _userClientCred.UserName.UserName = overrideUserName; + SecureString password = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPassword); + if (password != null) + _userClientCred.UserName.Password = password.ToUnsecureString(); + } + else + { + // Credentials Required. + ClaimsHomeRealmOptionsHomeRealm realm = HomeRealmServersList.GetServerByDisplayName(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.AuthHomeRealm)); + if (realm != null && !string.IsNullOrEmpty(realm.Uri)) + { + uUserHomeRealm = new Uri(realm.Uri); + + _tracer.Log(string.Format("HomeRealm is = {0}", uUserHomeRealm.ToString()), TraceEventType.Information); + + DeviceCredentials = new ClientCredentials(); + + DeviceCredentials.UserName.UserName = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName); + var securePassword = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPassword); + if (securePassword != null) + DeviceCredentials.UserName.Password = securePassword.ToUnsecureString(); + + if (_userClientCred == null) + _userClientCred = new ClientCredentials(); + + string overrideUserName = string.Empty; + if (!string.IsNullOrWhiteSpace(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDomain))) + { + // Domain is present... see if the user typed it into the user name box too... + if (!StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName).Contains( + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDomain))) + { + // they don't match.. prepend it.. + overrideUserName = string.Format("{0}\\{1}", + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDomain), + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName)); + + } + } + + if (string.IsNullOrWhiteSpace(overrideUserName)) + _userClientCred.UserName.UserName = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName); + else + _userClientCred.UserName.UserName = overrideUserName; + SecureString password = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPassword); + if (password != null) + _userClientCred.UserName.Password = password.ToUnsecureString(); + } + else + uUserHomeRealm = null; + } + + // This path Attempts to login in the user + if (selectedOrg != null) + { + return ConnectAndInitOrgService(selectedOrg); + } + + // Connecting to an OnPrem Server here. + _bgWorker.ReportProgress(30, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_PROCESS_CONNECT_TO_UII)); + + // Build Discovery Server connection URL + string CrmUrl = string.Empty; + if (!string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPort))) + { + CrmUrl = String.Format(CultureInfo.InvariantCulture, + "{0}://{1}:{2}/XRMServices/2011/Discovery.svc", + IsSSLReq ? Uri.UriSchemeHttps : Uri.UriSchemeHttp, + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmServerName), + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPort)); + } + else + { + CrmUrl = String.Format(CultureInfo.InvariantCulture, + "{0}://{1}/XRMServices/2011/Discovery.svc", + IsSSLReq ? Uri.UriSchemeHttps : Uri.UriSchemeHttp, + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmServerName)); + } + + _tracer.Log($"Discovery URI is = {CrmUrl}", TraceEventType.Information); + + // Discover Orgs Url. + Uri uCrmUrl = new Uri(CrmUrl); + + // Create the CRM Service Connection. + _bgWorker.ReportProgress(25, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_PROCESS_GET_ORGS)); + + + // This will try to discover any organizations that the user has access too, one way supports AD / IFD and the other supports Claims + DiscoverOrganizationsResult discoverOrganizationsResult = null; + + if (IsOAuth == true) + { + //Reading authority and userId from Config file + if (!string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.Authority))) + _cachedAuthorityName = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.Authority); + + if (!string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.UserId))) + _cachedUserId = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.UserId); + + if (ForceFirstOAuthPrompt) + { + _cachedAuthorityName = null; + _cachedUserId = null; + ForceFirstOAuthPrompt = false; + discoverOrganizationsResult = ServiceClient.DiscoverOnPremiseOrganizationsAsync(uCrmUrl, _userClientCred, ClientId, RedirectUri, _cachedAuthorityName, Client.Auth.PromptBehavior.Always, false, TokenCachePath).ConfigureAwait(false).GetAwaiter().GetResult(); + } + else + { + if (UserId == null && _cachedUserId != null) + UserId = new UserIdentifier(_cachedUserId, UserIdentifierType.RequiredDisplayableId); + discoverOrganizationsResult = ServiceClient.DiscoverOnPremiseOrganizationsAsync(uCrmUrl, _userClientCred, ClientId, RedirectUri, _cachedAuthorityName, _promptBehavior, false, TokenCachePath).ConfigureAwait(false).GetAwaiter().GetResult(); + } + } + else + { + throw new NotSupportedException(); + } + /* Not supported + else if (uUserHomeRealm == null) + orgs = ServiceClient.DiscoverOnPremiseOrganizationsAsync(uCrmUrl, null, userCred).ConfigureAwait(false).GetAwaiter().GetResult(); + else + orgs = ServiceClient.DiscoverOnPremiseOrganizationsAsync(uCrmUrl, uUserHomeRealm, userClientCred, DeviceCredentials).ConfigureAwait(false).GetAwaiter().GetResult(); ; + */ + + // Check the Result to see if we have Orgs back + if (discoverOrganizationsResult.OrganizationDetailCollection != null && discoverOrganizationsResult.OrganizationDetailCollection.Count > 0) + { + _tracer.Log($"Found {discoverOrganizationsResult.OrganizationDetailCollection.Count} Org(s)", TraceEventType.Information); + if (discoverOrganizationsResult.OrganizationDetailCollection.Count == 1) + { + OrgByServer orgByServer = new OrgByServer() { DiscoveryServerName = "Premise", OrgDetail = discoverOrganizationsResult.OrganizationDetailCollection.First() }; + AddOrgToOrgList(discoverOrganizationsResult.OrganizationDetailCollection.First(), Resources.LOGIN_FRM_CRM_PREM_NAME, uCrmUrl); + return ConnectAndInitOrgService(orgByServer, discoverOrganizationsResult.Account); + } + else + { + string userOrg = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg); + if (!string.IsNullOrEmpty(userOrg)) + { + _tracer.Log($"looking for Org = {userOrg} in the results from CRM's Discovery server list.", TraceEventType.Information); + // Find the Stored org in the returned collection.. + OrganizationDetail orgDetail = Utilities.DeterminOrgDataFromOrgInfo(discoverOrganizationsResult.OrganizationDetailCollection, userOrg); + + if (orgDetail != null && !string.IsNullOrEmpty(orgDetail.UniqueName)) + { + // Found it .. + _tracer.Log($"found User Org = {userOrg} in results", TraceEventType.Information); + // Good Find, Clear org list. + ClearOrgList(); + OrgByServer orgByServer = new OrgByServer() { DiscoveryServerName = "Premise", OrgDetail = orgDetail }; + return ConnectAndInitOrgService(orgByServer, discoverOrganizationsResult.Account); + } + } + + // Didn't find it in the list.. ask the user for the org. + foreach (OrganizationDetail od in discoverOrganizationsResult.OrganizationDetailCollection) + { + AddOrgToOrgList(od, Resources.LOGIN_FRM_CRM_PREM_NAME, uCrmUrl); + } + + return false; + } + } + else + { + // No Orgs detected. + ClearOrgList(); + _tracer.Log("No Orgs Found", TraceEventType.Information); + ErrorLogger.WriteToFile(new System.ArgumentException(Messages.CRMCONNECT_LOGIN_NO_ORGS_FOUND_PREM, StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmServerName))); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_NO_ORGS_FOUND_PREM, false, new System.ArgumentException(Messages.CRMCONNECT_LOGIN_NO_ORGS_FOUND_PREM, StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmServerName)))); + _tracer.Log(string.Format("No Orgs Found. Server Setting = {0}", StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmServerName)) + , TraceEventType.Error); + return false; + + } + + #endregion + } + else + { + #region OnLine / Live Code / OAuth + // Generate Live ID Connection Info + // GenerateDeviceCreds(); + + var liveCreds = new ClientCredentials(); + if (UseDefaultCreds) + { + _tracer.Log("Utilizing Current user to attempt login", TraceEventType.Information); + //Setting Use Default Credentials to false if this info is required further in code. + //StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.UseDefaultCreds, "False"); + } + else + { + liveCreds.UserName.UserName = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName); + SecureString password = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPassword); + if (password != null) + liveCreds.UserName.Password = password.ToUnsecureString(); + } + + if (!(string.IsNullOrEmpty(ClientId) || RedirectUri == null)) + { + //Switching to online when clientId & redirectUri are null/empty and Username and password are present, otherwise OAuth + IsOAuth = true; + } + + if (!bool.TryParse(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.AdvancedCheck), out IsAdvancedCheckEnabled)) + { + IsAdvancedCheckEnabled = false; + } + if (!IsAdvancedCheckEnabled) + { + //Setting Use Default Credentials to false if this info is required further in code. + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion, ""); + liveCreds.UserName.Password = string.Empty; + liveCreds.UserName.UserName = string.Empty; + } + + // THIS IS FOR CONNECTING TO AN ON-LINE SERVER + _bgWorker.ReportProgress(30, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_PROCESS_CONNECT_TO_UII)); + + if (_isSkipDiscovery) + { + string _baseWebApiUriFormat = @"{0}/api/data/v{1}/"; + string _baseSoapOrgUriFormat = @"{0}/XRMServices/2011/Organization.svc"; + + EndpointCollection ep = new EndpointCollection(); + ep.Add(EndpointType.WebApplication, _directConnectUri); + ep.Add(EndpointType.OrganizationDataService, string.Format(_baseWebApiUriFormat, _directConnectUri, "8.0")); + ep.Add(EndpointType.OrganizationService, string.Format(_baseSoapOrgUriFormat, _directConnectUri)); + + OrganizationDetail d = new OrganizationDetail(); + d.FriendlyName = "DIRECTSET"; + d.OrganizationId = Guid.Empty; + d.OrganizationVersion = "0.0.0.0"; + d.State = OrganizationState.Enabled; + d.UniqueName = "HOLD"; + d.UrlName = "HOLD"; + System.Reflection.PropertyInfo proInfo = d.GetType().GetProperty("Endpoints"); + if (proInfo != null) + { + proInfo.SetValue(d, ep, null); + } + + selectedOrg = new OrgByServer(); + selectedOrg.OrgDetail = d; + + return ConnectAndInitOrgService(selectedOrg); + } + + // This path Attempts to login in the user + if (selectedOrg != null) + { + return ConnectAndInitOrgService(selectedOrg); + } + + var discoverResult = FindOnlineDiscoveryServer(liveCreds); + + if (_orgListView.OrgsList != null && _orgListView.OrgsList.Count > 0) + { + _tracer.Log($"Found {_orgListView.OrgsList.Count} Org(s)", TraceEventType.Information); + if (_orgListView.OrgsList.Count == 1) + { + bool success = ConnectAndInitOrgService(_orgListView.OrgsList.First(), discoverResult.Account); + if (success) + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion, + OnlineDiscoveryServerList.GetServerShortNameByDisplayName(_orgListView.OrgsList.First().DiscoveryServerName, _deploymentType == CrmDeploymentType.O365)); +#if DEBUG + TestingHelper.Instance.SelectedOption = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion); +#endif + return success; + } + else + { + string userOrg = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg); + if (!string.IsNullOrEmpty(userOrg)) + { + _tracer.Log($"looking for Org = {userOrg} in the results from CRM's Discovery server list.", TraceEventType.Information); + // Find the Stored org in the returned collection.. + var orgDetail = CrmOrgList.DeterminOrgDataFromOrgInfo(_orgListView, userOrg); + + if (orgDetail != null && !string.IsNullOrEmpty(orgDetail.OrgDetail.UniqueName)) + { + // Found it .. + _tracer.Log($"found User Org = {userOrg} in results", TraceEventType.Information); + bool success = ConnectAndInitOrgService(orgDetail, discoverResult.Account); + if (success) + { + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion, + OnlineDiscoveryServerList.GetServerShortNameByDisplayName(orgDetail.DiscoveryServerName, _deploymentType == CrmDeploymentType.O365)); +#if DEBUG + TestingHelper.Instance.SelectedOption = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion); +#endif + // Good connect.. clear out the org list. + ClearOrgList(); + } + + return success; + + } + } + return false; + } + } + else + { + // Error here. + _tracer.Log("No Orgs Found", TraceEventType.Information); + + ErrorLogger.WriteToFile(new System.ArgumentException(Messages.CRMCONNECT_LOGIN_NO_ORGS_FOUND_ONLINE, StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion))); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_NO_ORGS_FOUND_ONLINE, false, new System.ArgumentException(Messages.CRMCONNECT_LOGIN_NO_ORGS_FOUND_ONLINE, StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion)))); + _tracer.Log(string.Format("No Orgs Found, Searched Online. Region Setting = {0}", StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion)) + , TraceEventType.Error); + return false; + } + #endregion + } + } + + #region Login / Discovery Server Exception handlers + + catch (MessageSecurityException ex) + { + // Login to Live Failed. + ErrorLogger.WriteToFile(ex); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(string.Format(Messages.CRMCONNECT_MSG_INVALID_LOGIN_DETAILS, StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg), false, ex.InnerException == null ? ex : ex.InnerException))); + _tracer.Log(string.Format(Messages.CRMCONNECT_MSG_INVALID_LOGIN_DETAILS, StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg), false, ex), TraceEventType.Error, ex); + return false; + + } + catch (SoapException exp) + { + // Log Error to file. + ErrorLogger.WriteToFile(exp); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(string.Format(Messages.CRMCONNECT_LOGIN_UNABLE_TO_CONNECT_GENERAL, StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg), false, exp.InnerException == null ? exp : exp.InnerException))); + _tracer.Log(string.Format(Messages.CRMCONNECT_LOGIN_UNABLE_TO_CONNECT_GENERAL, StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg), false, exp), TraceEventType.Error, exp); + return false; + } + catch (WebException webEx) + { + // Log Error to file. + ErrorLogger.WriteToFile(webEx); + // Check the result for Errors. + if (!string.IsNullOrEmpty(webEx.Message) && webEx.Message.Contains("HTTP status 401")) + { + // Login Error. + Debug.WriteLine(string.Format("Exception in Login handler : {0} \n{1}", webEx.Message, webEx.InnerException != null ? webEx.InnerException.StackTrace : string.Empty)); + _bgWorker.ReportProgress(100, + new ServerConnectStatusEventArgs( + string.Format(Messages.CRMCONNECT_MSG_INVALID_LOGIN_DETAILS_PARAMS, webEx.Message).Replace("\\n", Environment.NewLine), false, webEx.InnerException != null ? webEx.InnerException : webEx)); + _tracer.Log(string.Format(Messages.CRMCONNECT_MSG_INVALID_LOGIN_DETAILS_PARAMS, webEx.Message).Replace("\\n", Environment.NewLine), + TraceEventType.Error, webEx); + return false; + } + else + { + _bgWorker.ReportProgress(100, + new ServerConnectStatusEventArgs( + string.Format(Messages.CRMCONNECT_LOGIN_UNABLE_TO_CONNECT_GENERAL, StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg)), false, webEx.InnerException != null ? webEx.InnerException : webEx)); + _tracer.Log(string.Format(Messages.CRMCONNECT_LOGIN_UNABLE_TO_CONNECT_GENERAL, StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg)), + TraceEventType.Error, webEx); + return false; + } + } + catch (InvalidOperationException ex) + { + ErrorLogger.WriteToFile(ex); + _bgWorker.ReportProgress(100, + new ServerConnectStatusEventArgs( + string.Format(Messages.CRMCONNECT_LOGIN_UNABLE_TO_CONNECT_GENERAL, StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg)), false, ex.InnerException != null ? ex.InnerException : ex)); + _tracer.Log( + string.Format(Messages.CRMCONNECT_LOGIN_UNABLE_TO_CONNECT_GENERAL, StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg)), + TraceEventType.Error, ex); + return false; + } + catch (Exception ex) + { + ErrorLogger.WriteToFile(ex); + + var crmDeploymentType = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDeploymentType); + string message; + if (crmDeploymentType != null && crmDeploymentType.Equals(CrmDeploymentType.Prem.ToString(), StringComparison.OrdinalIgnoreCase)) + message = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg); + else + message = crmDeploymentType; + _bgWorker.ReportProgress(100, + new ServerConnectStatusEventArgs( + string.Format(Messages.CRMCONNECT_LOGIN_UNABLE_TO_CONNECT_GENERAL, message), false, ex.InnerException != null ? ex.InnerException : ex)); + _tracer.Log(string.Format(Messages.CRMCONNECT_LOGIN_UNABLE_TO_CONNECT_GENERAL, StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg)), + TraceEventType.Error, ex); + return false; + } + #endregion + } + + /// + /// Check for an Override OAuth Settings of the ForceOSDP flag is present in the supporting applications app.config file. + /// + internal void ResetOAuthIfOSDPOverrideSwitch() + { + if (ConfigurationManager.AppSettings != null) + if (ConfigurationManager.AppSettings["ForceOSDP"] != null && ConfigurationManager.AppSettings["ForceOSDP"].Equals("true", StringComparison.OrdinalIgnoreCase)) + { + // invalidate the current client ID and redirect URI settings. + ClientId = string.Empty; + RedirectUri = null; + }; + } + + /// + /// Clears the Found Organizations List + /// + private void ClearOrgList() + { + // Clear out existing Orgs. + if (ParentControl != null) + // try to clear it on the parent control. + ParentControl.Dispatcher.Invoke(DispatcherPriority.Normal, + new Action(_orgListView.OrgsList.Clear) + ); + else + { + try + { + _orgListView.OrgsList.Clear(); + } + catch (Exception ex) + { + // this will most likely occur if the OrgsList is attached to a different thread. + _tracer.Log(string.Format("Cannot clear the OrgsList object. Exception is = {0}", ex.Message), TraceEventType.Error); + } + } + } + + + /// + /// Performs Validation on the authentication request, looking for problems that can be solved before the initial connect attempt + /// + /// True if Ok, False if not + private bool ValidateUserSpecifiedData() + { + // Check Login Type vs Required CRM Connection Information. + if (!string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDeploymentType)) && + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDeploymentType).Equals(CrmDeploymentType.Prem.ToString(), StringComparison.OrdinalIgnoreCase)) + { + // if the on Prem Bit is checked make sure there server name is there. + if (string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmServerName))) + { + ErrorLogger.WriteToFile(new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_SERVER, Dynamics_ConfigFileServerKeys.CrmServerName.ToString())); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_SERVER_MSG, false, new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_SERVER))); + _tracer.Log("You must specify a CRM Server to connect too", TraceEventType.Information); + return false; + } + } + + // Check Login flag vs Required Information based on connection Type. + if (!string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.UseDefaultCreds)) && + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.UseDefaultCreds).Equals("false", StringComparison.CurrentCultureIgnoreCase)) + { + bool IsClientIdOrRedirectUriEmpty = string.IsNullOrEmpty(ClientId) || RedirectUri == null; + bool IsUserNameNull = string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName)); + var passwordSecureString = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPassword); + bool IsPasswordNull = (passwordSecureString == null) || (passwordSecureString.Length == 0); + if (!bool.TryParse(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.AdvancedCheck), out IsAdvancedCheckEnabled)) + { + IsAdvancedCheckEnabled = false; + } + + if (!string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.AuthHomeRealm)) && + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.AuthHomeRealm).Equals(Resources.LOGIN_FRM_AUTHTYPE_OAUTH)) + { + // Use Default is not checked and Auth type is OAuth. + if (IsUserNameNull && !IsPasswordNull) + { + ErrorLogger.WriteToFile(new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID, Dynamics_ConfigFileServerKeys.CrmUserName.ToString())); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID_MSG, + false, new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID))); + _tracer.Log("You must specify both User Name and Password or both are required to be null", TraceEventType.Information); + return false; + } + else if (!IsUserNameNull && IsPasswordNull) + { + ErrorLogger.WriteToFile(new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_PASSWORD, Dynamics_ConfigFileServerKeys.CrmPassword.ToString())); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_PASSWORD_MSG, + false, new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_PASSWORD))); + _tracer.Log("You must specify both User Name and Password or both are required to be null", TraceEventType.Information); + return false; + } + + if (IsClientIdOrRedirectUriEmpty) + { + ErrorLogger.WriteToFile(new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID, Dynamics_ConfigFileServerKeys.CrmUserName.ToString())); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID_MSG, + false, new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID))); + _tracer.Log("You must specify both User Name and Password", TraceEventType.Information); + return false; + } + } + else if(!string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDeploymentType)) && + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDeploymentType).Equals(CrmDeploymentType.O365.ToString(), StringComparison.OrdinalIgnoreCase) && + IsAdvancedCheckEnabled) + { + // Use Default is not checked and Auth type is OAuth. + if (IsUserNameNull && !IsPasswordNull) + { + ErrorLogger.WriteToFile(new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID, Dynamics_ConfigFileServerKeys.CrmUserName.ToString())); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID_MSG, + false, new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID))); + _tracer.Log("You must specify both User Name and Password or both are required to be null", TraceEventType.Information); + return false; + } + else if (!IsUserNameNull && IsPasswordNull) + { + ErrorLogger.WriteToFile(new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_PASSWORD, Dynamics_ConfigFileServerKeys.CrmPassword.ToString())); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_PASSWORD_MSG, + false, new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_PASSWORD))); + _tracer.Log("You must specify both User Name and Password or both are required to be null", TraceEventType.Information); + return false; + } + + if (IsClientIdOrRedirectUriEmpty && IsUserNameNull && IsPasswordNull) + { + ErrorLogger.WriteToFile(new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID, Dynamics_ConfigFileServerKeys.CrmUserName.ToString())); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID_MSG, + false, new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID))); + _tracer.Log("You must specify both User Name and Password", TraceEventType.Information); + return false; + } + } + else if((!string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDeploymentType)) && + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDeploymentType).Equals(CrmDeploymentType.O365.ToString(), StringComparison.OrdinalIgnoreCase)) && + !IsAdvancedCheckEnabled) + { + if (IsClientIdOrRedirectUriEmpty) + { + //need to add a constant for a new error message, get it scrubbed and localise it. + ErrorLogger.WriteToFile(new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_PASSWORD, Dynamics_ConfigFileServerKeys.CrmPassword.ToString())); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_PASSWORD_MSG, + false, new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_PASSWORD))); + _tracer.Log("You must specify both clientId and RedirectUri", TraceEventType.Information); + return false; + } + } + else + { + // Use Default is not checked.. + // if the on useDefualt Creds is not checked make sure the user name is there. + if (string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName))) + { + ErrorLogger.WriteToFile(new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID, Dynamics_ConfigFileServerKeys.CrmUserName.ToString())); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID_MSG, + false, new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_USERID))); + _tracer.Log("You must specify a User Name", TraceEventType.Information); + return false; + } + + if (StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPassword) == null) + { + ErrorLogger.WriteToFile(new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_PASSWORD, Dynamics_ConfigFileServerKeys.CrmPassword.ToString())); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_PASSWORD_MSG, + false, new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_PASSWORD))); + _tracer.Log("You must specify a Password", TraceEventType.Information); + return false; + } + } + } + else + { + // Use Default is checked. + // if Auth Type is not AD,IFD or OAuth Then Alert the user. + if (!string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.AuthHomeRealm))) + { + if (!(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.AuthHomeRealm).Equals(Resources.LOGIN_FRM_AUTHTYPE_AD) || + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.AuthHomeRealm).Equals(Resources.LOGIN_FRM_AUTHTYPE_IFD) || + StorageUtils.GetConfigKey(ServerConfigKeys,Dynamics_ConfigFileServerKeys.AuthHomeRealm).Equals(Resources.LOGIN_FRM_AUTHTYPE_OAUTH))) + { + ErrorLogger.WriteToFile(new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_LOGINTYPE, Dynamics_ConfigFileServerKeys.CrmPassword.ToString())); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_LOGINTYPE_INCOMPATIBLE_LOGIN_TYPE, false, new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_LOGINTYPE))); + _tracer.Log("Invalid Login Type and Credentials", TraceEventType.Information); + return false; + } + } + else + { + // No Auth type specified.. + ErrorLogger.WriteToFile(new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_AUTHTYPE, Dynamics_ConfigFileServerKeys.CrmPassword.ToString())); + _bgWorker.ReportProgress(100, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_AUTHTYPE_MSG, false, new System.ArgumentException(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_AUTHTYPE))); + _tracer.Log("Invalid Auth Type and Credentials", TraceEventType.Information); + return false; + } + } + return true; + } + + /// + /// Adds an Org to the List of Orgs + /// + /// + /// + /// + private void AddOrgToOrgList(OrganizationDetail organizationDetail, string discoveryServer, Uri discoveryServerUri) + { + _tracer.Log("AddOrgToOrgList()", TraceEventType.Start); + // Not in the UI Thread. + if (_orgListView == null) _orgListView = new CrmOrgList(); + if (_orgListView.OrgsList == null) _orgListView.OrgsList = new ObservableCollection(); + + if (ParentControl != null) + ParentControl.Dispatcher.Invoke((Action)(() => + { + _orgListView.OrgsList.Add(new OrgByServer() + { + DiscoveryServerName = discoveryServer, + OrgDetail = organizationDetail + }); + }), DispatcherPriority.Send); + else + _orgListView.OrgsList.Add(new OrgByServer() + { + DiscoveryServerName = discoveryServer, + OrgDetail = organizationDetail + }); + + _tracer.Log("AddOrgToOrgList()", TraceEventType.Stop); + } + + /// + /// Adds an Org to the List of Orgs + /// + /// + /// + /// + private void AddOrgToOrgList(OrganizationDetailCollection organizationDetailList, string discoveryServer, Uri discoveryServerUri) + { + foreach (OrganizationDetail o in organizationDetailList) + { + AddOrgToOrgList(o, discoveryServer, discoveryServerUri); + } + } + + /// + /// Iterates through the list of CRM online Discovery Servers to find one that knows the user. + /// + /// + private DiscoverOrganizationsResult FindOnlineDiscoveryServer(ClientCredentials liveCreds) + { + _tracer.Log("FindCrmOnlineDiscoveryServer()", TraceEventType.Start); + if (IsOAuth == true) + { + //Reading authority and userId from Config file + if (!string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.Authority))) + _cachedAuthorityName = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.Authority); + + if (!string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.UserId))) + _cachedUserId = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.UserId); + } + + DiscoverOrganizationsResult discoverResult = null; + // If the user as specified a server to use, try to get the org from that server. + if (!string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion))) + { + _tracer.Log("Using User Specified Server ", TraceEventType.Information); + // Server specified... + OnlineDiscoveryServer svr = OnlineDiscoveryServerList.GetServerByShortName(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion), _deploymentType == CrmDeploymentType.O365); + if (svr != null) + { + if (IsOAuth == true && svr.RequiresRegionalDiscovery) + { + if (svr.RegionalGlobalDiscoveryServer == null) + { + _tracer.Log(string.Format("Trying Discovery Server, ({1}) URI is = {0}", svr.DiscoveryServer.ToString(), svr.DisplayName), TraceEventType.Information); + _bgWorker.ReportProgress(25, new ServerConnectStatusEventArgs(string.Format(Messages.CRMCONNECT_LOGIN_PROCESS_GET_ORGS_LIVE, svr.DisplayName))); + discoverResult = QueryOAuthDiscoveryServer(svr.DiscoveryServer, liveCreds, UserId, ClientId, RedirectUri, _promptBehavior, TokenCachePath); + } + else + { + // using regional version of a GD. + _tracer.Log(string.Format(CultureInfo.InvariantCulture, "Trying Regional Global Discovery Server, ({1}) URI is = {0}", svr.RegionalGlobalDiscoveryServer.ToString(), svr.DisplayName), TraceEventType.Information); + _bgWorker.ReportProgress(25, new ServerConnectStatusEventArgs(string.Format(Messages.CRMCONNECT_LOGIN_PROCESS_GET_ORGS_LIVE, svr.DisplayName))); + return QueryOnlineServerList(OnlineDiscoveryServerList.OSDPServers, liveCreds, trimToDiscoveryUri: svr.DiscoveryServer, globalDiscoUriToUse: svr.RegionalGlobalDiscoveryServer); // using regional disco server. + } + } + else + { + if (IsOAuth) + { + return QueryOnlineServerList(OnlineDiscoveryServerList.OSDPServers, liveCreds, trimToDiscoveryUri: svr.DiscoveryServer); + } + } + if (discoverResult.OrganizationDetailCollection != null) + AddOrgToOrgList(discoverResult.OrganizationDetailCollection, svr.DisplayName, svr.DiscoveryServer); + + return discoverResult; + } + + } + + // Server is unspecified or the user chose ‘don’t know’ + if (_deploymentType == CrmDeploymentType.Online) + discoverResult = QueryOnlineServerList(OnlineDiscoveryServerList.Servers, liveCreds); + else + discoverResult = QueryOnlineServerList(OnlineDiscoveryServerList.OSDPServers, liveCreds); + + _tracer.Log("FindCrmOnlineDiscoveryServer()", TraceEventType.Stop); + return discoverResult; + } + + /// + /// Iterate over each discovery server in the collection + /// + /// Collection of discovery servers + /// Credential object + /// Forces the results to be trimed to this region when present + /// Overriding Global Discovery URI + private DiscoverOrganizationsResult QueryOnlineServerList(ObservableCollection svrs, ClientCredentials liveCreds, Uri trimToDiscoveryUri = null, Uri globalDiscoUriToUse = null) + { + DiscoverOrganizationsResult discoverResult = null; + // Execute Global Discovery + if (IsOAuth == true) + { + Uri globalDisoSvcUri = globalDiscoUriToUse != null ? new Uri(string.Format("{0}api/discovery/v{1}/{2}", globalDiscoUriToUse.ToString(), _globlaDiscoVersion, "Instances")) : new Uri(GlobalDiscoveryAllInstancesUri); + _tracer.Log(string.Format("Trying Global Discovery Server, ({1}) URI is = {0}", globalDisoSvcUri.ToString(), "Global Discovery"), TraceEventType.Information); + _bgWorker.ReportProgress(25, new ServerConnectStatusEventArgs(string.Format(Messages.CRMCONNECT_LOGIN_PROCESS_GET_ORGS_LIVE, "Global Discovery"))); + + try + { + discoverResult = QueryOAuthDiscoveryServer(globalDisoSvcUri, liveCreds, UserId, ClientId, RedirectUri, _promptBehavior, TokenCachePath, useGlobalDisco:true); + } + catch (MessageSecurityException) + { + _tracer.Log(string.Format("MessageSecurityException while trying to connect Discovery Server, ({1}) URI is = {0}", globalDisoSvcUri.ToString(), "Global Discovery"), TraceEventType.Warning); + return null; + } + catch (Exception ex) + { + _tracer.Log($"Exception while trying to connect Discovery Server, (Global Discovery) URI is = {globalDisoSvcUri}. Exception message: {ex.Message}", TraceEventType.Error); + throw; + } + + // if we have results.. add them to the AddOrgToOrgList object. ( need to iterate over the objects to match region to result. ) + + if (discoverResult.OrganizationDetailCollection != null) + { + bool isOnPrem = false; + foreach (var itm in discoverResult.OrganizationDetailCollection) + { + var orgObj = Utilities.DeterminDiscoveryDataFromOrgDetail(new Uri(itm.Endpoints[EndpointType.OrganizationService]), out isOnPrem, Geo: itm.Geo); + if (trimToDiscoveryUri != null && !trimToDiscoveryUri.Equals(orgObj.DiscoveryServerUri)) + continue; + AddOrgToOrgList(itm, orgObj.DisplayName, orgObj.DiscoveryServerUri); + } + } + } + else + { + foreach (var svr in svrs) + { + // Covers the "dont know" setting. + if (svr.DiscoveryServer == null) continue; + + _tracer.Log(string.Format("Trying Live Discovery Server, ({1}) URI is = {0}", svr.DiscoveryServer.ToString(), svr.DisplayName), TraceEventType.Information); + + _bgWorker.ReportProgress(25, new ServerConnectStatusEventArgs(string.Format(Messages.CRMCONNECT_LOGIN_PROCESS_GET_ORGS_LIVE, svr.DisplayName))); + + try + { + if (IsOAuth == true) + { + discoverResult = QueryOAuthDiscoveryServer(svr.DiscoveryServer, liveCreds, UserId, ClientId, RedirectUri, _promptBehavior, TokenCachePath); + } + } + catch (MessageSecurityException) + { + _tracer.Log(string.Format("MessageSecurityException while trying to connect Discovery Server, ({1}) URI is = {0}", svr.DiscoveryServer.ToString(), svr.DisplayName), TraceEventType.Warning); + return null; + } + catch (Exception) + { + _tracer.Log(string.Format("Exception while trying to connect Discovery Server, ({1}) URI is = {0}", svr.DiscoveryServer.ToString(), svr.DisplayName), TraceEventType.Error); + return null; + } + if (discoverResult.OrganizationDetailCollection != null) + AddOrgToOrgList(discoverResult.OrganizationDetailCollection, svr.DisplayName, svr.DiscoveryServer); + } + } + + return discoverResult; + } + + /// + /// Query an individual OAuth server + /// + /// Discovery Service Uri + /// Credentials supplied for login + /// User identifier + /// Registered Client Id of application trying for OAuth + /// Uri to redirect the application + /// Prompt behavior defining ADAL login popup + /// Token cache path supplied by user for storing bearer tokens + /// if true, calls global discovery path + /// + private DiscoverOrganizationsResult QueryOAuthDiscoveryServer(Uri discoServer, ClientCredentials liveCreds, UserIdentifier user, string clientId, Uri redirectUri, Client.Auth.PromptBehavior promptBehavior, string tokenCachePath, bool useGlobalDisco = false) + { + _tracer.Log($"{nameof(QueryOAuthDiscoveryServer)}", TraceEventType.Start); + + DiscoverOrganizationsResult result = null; + try + { + if (ForceFirstOAuthPrompt) + { + ForceFirstOAuthPrompt = false; + _cachedAuthorityName = null; + _cachedUserId = null; + promptBehavior = Client.Auth.PromptBehavior.Always; + + if (UseDefaultCreds) + promptBehavior = Client.Auth.PromptBehavior.SelectAccount; + + result = ServiceClient.DiscoverOnlineOrganizationsAsync(discoServer, liveCreds, clientId, redirectUri, false, _cachedAuthorityName, promptBehavior, UseDefaultCreds, tokenCachePath).ConfigureAwait(false).GetAwaiter().GetResult(); + } + else + { + if (user == null && _cachedUserId != null) + user = new UserIdentifier(_cachedUserId, UserIdentifierType.RequiredDisplayableId); + result = ServiceClient.DiscoverOnlineOrganizationsAsync(discoServer, liveCreds, clientId, redirectUri, false, _cachedAuthorityName, promptBehavior, tokenCacheStorePath:tokenCachePath).ConfigureAwait(false).GetAwaiter().GetResult(); + } + return result; + } + catch (SecurityAccessDeniedException securEx) + { + // User Does not have any orgs on this server. + _tracer.Log("User does not have access to this discovery server", TraceEventType.Warning, securEx); + } + _tracer.Log($"{nameof(QueryOAuthDiscoveryServer)}", TraceEventType.Stop); + + return result; + } + + /// + /// Connects too and inits the org Data service. + /// + /// Organization to use when connecting to CRM + /// account hint + private bool ConnectAndInitOrgService(OrgByServer orgdata, IAccount account = null) + { + _tracer.Log($"{nameof(ConnectAndInitOrgService)}", TraceEventType.Start); + + Uri oOrgSvc = BuildOrgConnectUri(orgdata.OrgDetail); + _tracer.Log(string.Format("Organization Service URI is = {0}", oOrgSvc.ToString()), TraceEventType.Information); + + // Set the Org into system config + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg, orgdata.OrgDetail.UniqueName); + + // Build User Credential + if (_deploymentType == CrmDeploymentType.Prem) + { + if (uUserHomeRealm != null) + { + // Claims server... + _bgWorker.ReportProgress(25, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_PROCESS_CONNNECTING)); + _tracer.Log(Messages.CRMCONNECT_LOGIN_PROCESS_CONNNECTING, TraceEventType.Information); + + ServiceClient = new ServiceClient(_userClientCred.UserName.UserName, ServiceClient.MakeSecureString(_userClientCred.UserName.Password), + uUserHomeRealm.ToString(), oOrgSvc.Host, oOrgSvc.Port.ToString(), orgdata.OrgDetail.UniqueName, IsSSLReq, useUniqueInstance: true, orgDetail: orgdata.OrgDetail, ClientId, RedirectUri); + + PopulateOrgProperties(orgdata); + + return ServiceClient.IsReady; + } + else if (IsOAuth == true) + { + _bgWorker.ReportProgress(25, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_PROCESS_CONNNECTING)); + _tracer.Log(Messages.CRMCONNECT_LOGIN_PROCESS_CONNNECTING, TraceEventType.Information); + + ServiceClient = new ServiceClient(_userClientCred.UserName.UserName, ServiceClient.MakeSecureString(_userClientCred.UserName.Password), + string.Empty, oOrgSvc.Host, oOrgSvc.Port.ToString(), + orgdata.OrgDetail.UniqueName, true, true, orgdata.OrgDetail, ClientId, RedirectUri, _promptBehavior, TokenCachePath); + + PopulateOrgProperties(orgdata); + + return ServiceClient.IsReady; + } + else + { + throw new NotSupportedException(); + /* + // Create the CRM Service Connection. Non-Claims + bgWorker.ReportProgress(25, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_PROCESS_CONNNECTING)); + tracer.Log(Messages.CRMCONNECT_LOGIN_PROCESS_CONNNECTING, TraceEventType.Information); + + // Connect to CRM Directly + CrmSvc = new ServiceClient(userCred, oOrgSvc.Host, oOrgSvc.Port.ToString(), orgdata.OrgDetail.UniqueName, useSsl: IsSSLReq, orgDetail: orgdata.OrgDetail, useUniqueInstance: true); + + PopulateOrgProperties(orgdata); + return CrmSvc.IsReady; + */ + } + } + else + { + //if (!bool.TryParse(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.UseDefaultCreds), out UseDefaultCreds)) + //{ + // UseDefaultCreds = false; + //} + // Connecting to Online via Live. + //if (UseDefaultCreds) + //{ + // // Error here .. Cannot use Default with Online. + // bgWorker.ReportProgress(25, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_LOGINTYPE_INCOMPATIBLE_LOGIN_TYPE)); + // tracer.Log(Messages.CRMCONNECT_LOGIN_VALIDATION_ERR_LOGINTYPE_INCOMPATIBLE_LOGIN_TYPE, TraceEventType.Error); + // PopulateOrgProperties(orgdata); + // return false; + //} + //else + if (IsOAuth == true) + { + _bgWorker.ReportProgress(25, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_PROCESS_CONNNECTING)); + _tracer.Log(Messages.CRMCONNECT_LOGIN_PROCESS_CONNNECTING, TraceEventType.Information); + + string toSendUserId = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName); + SecureString toSendSecurePW = StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPassword); + + // if the useDefaultCred is checked, and IsAdvanced is Checked. OR UseDefaultCreds is not checked and IsAdvanceds is not checked - + // Dont send UID / PW/ + if ((UseDefaultCreds && IsAdvancedCheckEnabled) || (!UseDefaultCreds && !IsAdvancedCheckEnabled)) + { + // Pass account as a hint + toSendUserId = account != null ? account.Username : string.Empty ; + toSendSecurePW = null; + } + + ServiceClient = new ServiceClient( + toSendUserId, + toSendSecurePW, + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion), + orgdata.OrgDetail.UniqueName, true, orgdata.OrgDetail, ClientId, RedirectUri, _promptBehavior, useDefaultCreds: UseDefaultCreds, TokenCachePath); + PopulateOrgProperties(orgdata); + return ServiceClient.IsReady; + } + else + { + _bgWorker.ReportProgress(25, new ServerConnectStatusEventArgs(Messages.CRMCONNECT_LOGIN_PROCESS_CONNNECTING)); + _tracer.Log(Messages.CRMCONNECT_LOGIN_PROCESS_CONNNECTING, TraceEventType.Information); + + if (_deploymentType == CrmDeploymentType.O365) + { + // Connect via o365 + ServiceClient = new ServiceClient( + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName), + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPassword), + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion), + orgdata.OrgDetail.UniqueName, useUniqueInstance: true, orgDetail: orgdata.OrgDetail, ClientId, RedirectUri); + if (ServiceClient.IsReady) + { + // Good connect Set Online Info for later recovery. + try + { + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion, + OnlineDiscoveryServerList.GetServerShortNameByDisplayName(orgdata.DiscoveryServerName, true)); + } + catch { } // Catch unknown errors here. + } + } + else + { + // Connect via Live + ServiceClient = new ServiceClient( + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName), + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPassword), + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion), + orgdata.OrgDetail.UniqueName, useUniqueInstance: true, orgDetail: orgdata.OrgDetail, ClientId, RedirectUri); + if (ServiceClient.IsReady) + { + // Good connect Set Online Info for later recovery. + try + { + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOnlineRegion, + OnlineDiscoveryServerList.GetServerShortNameByDisplayName(orgdata.DiscoveryServerName)); + } + catch { } // Catch unknown errors here. + } + } + PopulateOrgProperties(orgdata); + return ServiceClient.IsReady; + } + } + } + + /// + /// Populates the local properties of the connector. + /// + /// + private void PopulateOrgProperties(OrgByServer orgdata) + { + // Set Org name. + ConnectedOrgFriendlyName = ServiceClient != null && ServiceClient.IsReady && !string.IsNullOrWhiteSpace(ServiceClient.ConnectedOrgFriendlyName) ? ServiceClient.ConnectedOrgFriendlyName : orgdata.FriendlyName; + ConnectedOrgUniqueName = ServiceClient != null && ServiceClient.IsReady && !string.IsNullOrWhiteSpace(ServiceClient.ConnectedOrgUniqueName) ? ServiceClient.ConnectedOrgUniqueName : orgdata.OrgDetail.UniqueName; + ConnectedOrgPublishedEndpoints = ServiceClient != null && ServiceClient.IsReady && ServiceClient.ConnectedOrgPublishedEndpoints != null ? ServiceClient.ConnectedOrgPublishedEndpoints : orgdata.OrgDetail.Endpoints; + ConnectedOrgId = ServiceClient != null && ServiceClient.IsReady && ServiceClient.ConnectedOrgId != Guid.Empty? ServiceClient.ConnectedOrgId : orgdata.OrgDetail.OrganizationId; + } + + /// + /// Builds the Organization Service Connect URI + /// - This is done, potentially replacing the original string, to deal with the discovery service returning an unusable string, for example, a DNS name that does not resolve. + /// + /// Org Data found from the Discovery Service. + /// CRM Connection URI + private Uri BuildOrgConnectUri(OrganizationDetail orgdata) + { + _tracer.Log("BuildOrgConnectUri()", TraceEventType.Start); + // determine if SSL is required to connect. + bool IsSSLReq = false; + if (!bool.TryParse(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUseSSL), out IsSSLReq)) + IsSSLReq = false; + + // Build connection URL + string CrmUrl = string.Empty; + Uri OrgEndPoint = new Uri(orgdata.Endpoints[EndpointType.OrganizationService]); + _tracer.Log("DiscoveryServer indicated organization service location = " + OrgEndPoint.ToString(), TraceEventType.Verbose); +#if DEBUG + if (TestingHelper.Instance.IsDebugEnvSelected()) + { + return OrgEndPoint; + } +#endif + if (Utilities.IsValidOnlineHost(OrgEndPoint)) + { + // CRM Online ..> USE PROVIDED URI. + _tracer.Log("BuildOrgConnectUri()", TraceEventType.Stop); + return OrgEndPoint; + } + else + { + if (_deploymentType == CrmDeploymentType.O365) + { + return OrgEndPoint; // O365 returns direct org end point. + } + + // Need to come up with a way to support a Redirect from the Discovery Server,,, IE: the discovery server is in one location, the server in another. + // and continue to support the concept of a rewrite of the URL. + // perhaps try to resolve the URL the discovery server returns? + // On Fail, do the URL Rewrite. + + string _InternetProtocalToUse = IsSSLReq ? Uri.UriSchemeHttps : Uri.UriSchemeHttp; + if (!OrgEndPoint.Scheme.Equals(_InternetProtocalToUse, StringComparison.OrdinalIgnoreCase)) + { + _tracer.Log("Organization Services is using a different URI Scheme then requested, switching to Discovery server specified scheme = " + OrgEndPoint.Scheme, TraceEventType.Stop); + _InternetProtocalToUse = OrgEndPoint.Scheme; + } + + if (!string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPort))) + { + CrmUrl = String.Format(CultureInfo.InvariantCulture, + "{0}://{1}:{2}{3}", + _InternetProtocalToUse, + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmServerName), + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPort), + OrgEndPoint.PathAndQuery + ); + } + else + { + CrmUrl = String.Format(CultureInfo.InvariantCulture, + "{0}://{1}{2}", + _InternetProtocalToUse, + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmServerName), + OrgEndPoint.PathAndQuery); + } + } + + _tracer.Log("BuildOrgConnectUri()", TraceEventType.Stop); + return new Uri(CrmUrl); + } + + + + + #region ConfigFile Commands + + /// + /// Will remove the user connection data settings from the users app config directory, useable only when the UseUserLocalDirectoryForConfigStore is set to true. + /// this should be called after the connection has been established, or before the connect attempt in order to be effective. + /// + /// true on success, false on fail. + public bool RemoveUserLocalDirectoryConfigFile() + { + try + { + // + if (UseUserLocalDirectoryForConfigStore) + { + // User Specified to use the users app config directory to store the user connect info. + string sPotentialPath = string.Empty; + if (string.IsNullOrWhiteSpace(HostApplicatioNameOveride)) + sPotentialPath = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft", + AppDomain.CurrentDomain.FriendlyName.Replace(".vshost", "").Replace(".exe", "")); + else + sPotentialPath = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft", + HostApplicatioNameOveride.Replace(".vshost", "").Replace(".exe", "")); + + string ConfigPath = string.Empty; + if (string.IsNullOrWhiteSpace(HostApplicatioNameOveride)) + ConfigPath = Path.Combine(sPotentialPath, AppDomain.CurrentDomain.FriendlyName.Replace(".vshost", "")); + else + ConfigPath = Path.Combine(sPotentialPath, HostApplicatioNameOveride.Replace(".vshost", "")); + + if (File.Exists(ConfigPath)) + { + File.Delete(ConfigPath); + return true; + } + } + else + { + _tracer.Log("Cannot call RemoveUserLocalDirectoryConfigFile when UseUserLocalDirectoryForConfigStore is false", TraceEventType.Warning); + return false; + } + } + catch (Exception ex) + { + _tracer.Log("Exception raised in RemoveUserLocalDirectoryConfigFile", TraceEventType.Error, ex); + } + return false; + } + + /// + /// Loads the Configuration key's from file b/c some things are missing + /// + public Dictionary LoadConfigFromFile(bool readLocalFirst = false) + { + _tracer.Log("LoadConfigFromFile()", TraceEventType.Start); + if (ServerConfigKeys != null) + ServerConfigKeys.Clear(); + else + ServerConfigKeys = new Dictionary(); + try + { + // Get a handle to the Configuration + string ConfigPath = string.Empty; + bool iCreatedFile = false; + if (UseUserLocalDirectoryForConfigStore && !readLocalFirst) + { + // User Specified to use the users app config directory to store the user connect info. + string sPotentialPath = string.Empty; + if (string.IsNullOrWhiteSpace(HostApplicatioNameOveride)) + sPotentialPath = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft", + AppDomain.CurrentDomain.FriendlyName.Replace(".vshost", "").Replace(".exe", "")); + else + sPotentialPath = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft", + HostApplicatioNameOveride.Replace(".vshost", "").Replace(".exe", "")); + + if (!Directory.Exists(sPotentialPath)) + { + Directory.CreateDirectory(sPotentialPath); + } + + // Set logging directory for errors. + ErrorLogger.LogfileDirectoryOverride = sPotentialPath; + + if (string.IsNullOrWhiteSpace(HostApplicatioNameOveride)) + { + ConfigPath = Path.Combine(sPotentialPath, string.Format("{0}_{1}", ProfileName, AppDomain.CurrentDomain.FriendlyName.Replace(".vshost", "").Trim())); + TokenCachePath = Path.Combine(sPotentialPath, string.Format("{0}_{1}.{2}", ProfileName, AppDomain.CurrentDomain.FriendlyName.Replace(".vshost", "").Replace(".exe", "").Trim(), "tokens.dat")); + } + else + { + ConfigPath = Path.Combine(sPotentialPath, string.Format("{0}_{1}", ProfileName, HostApplicatioNameOveride.Replace(".vshost", "").Trim())); + TokenCachePath = Path.Combine(sPotentialPath, string.Format("{0}_{1}.{2}", ProfileName, HostApplicatioNameOveride.Replace(".vshost", "").Replace(".exe", "").Trim(), "tokens.dat")); + } + } + else + { + ConfigPath = Environment.CommandLine.Replace("\"", "").Replace(".vshost", "").Trim(); + // when local directory store is not used, application executable path shall be used to store the token cache. Inline with the config file + TokenCachePath = string.Format("{0}.{1}", Environment.CommandLine.Replace("\"", "").Replace(".vshost", "").Replace(".exe", "").Trim(),"tokens.dat"); + } + + if (!File.Exists(ConfigPath) && !readLocalFirst) + { + // Need to create the file .. + // This is to allow the configuration reader to work right ( because I didn't want to rewrite the load / save system for this use case yet. ) + using (StreamWriter cfgFileWr = File.CreateText(ConfigPath)) + { + cfgFileWr.Flush(); + } + + if (UseUserLocalDirectoryForConfigStore) + { + try + { + // user is using a specified file directory... encrypt file to user using Machine / FS Locking. + // this will lock / prevent users other then the current user from accessing this file. + FileInfo fi = new FileInfo(ConfigPath); + fi.Encrypt(); + } + catch (IOException) + { + // This can happen when a certificate system on the host has failed. + // usually this can be fixed with the steps in this article : http://support.microsoft.com/kb/937536 + //tracer.Log("Failed to Encrypt Configuration File!", TraceEventType.Error, encrEX); + //tracer.Log("This problem may be related to a domain certificate in windows being out of sync with the domain, please read http://support.microsoft.com/kb/937536"); + } + catch (Exception) + { + //tracer.Log("Failed to Encrypt Configuration File!", TraceEventType.Error, genEX); + } + + } + iCreatedFile = true; + } + + if (UseUserLocalDirectoryForConfigStore && !readLocalFirst) + { + // User Specified to use the users app config directory to store the user connect Info. + // Map Local file only if I created it. + if (iCreatedFile) + { + // Read the config settings right now.. + ServerConfigKeys = LoadConfigFromFile(true); + if (ServerConfigKeys == null) // Deal with failure. + ServerConfigKeys = new Dictionary(); + + // Save the template. + if (ServerConfigKeys != null) + SaveConfigToFile(ServerConfigKeys); + } + } + + + if (File.Exists(ConfigPath)) + { + Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigPath); + if (config != null) + { + SetServerConfigKey(config, Dynamics_ConfigFileServerKeys.CrmOrg, overrideDefaultSet: readLocalFirst); + SetServerConfigKey(config, Dynamics_ConfigFileServerKeys.CrmServerName, overrideDefaultSet: readLocalFirst); + SetServerConfigKey(config, Dynamics_ConfigFileServerKeys.CrmPort, overrideDefaultSet: readLocalFirst); + SetServerConfigKey(config, Dynamics_ConfigFileServerKeys.CrmOnlineRegion, overrideDefaultSet: readLocalFirst); + SetServerConfigKey(config, Dynamics_ConfigFileServerKeys.AuthHomeRealm, overrideDefaultSet: readLocalFirst); + SetServerConfigKey(config, Dynamics_ConfigFileServerKeys.CrmDeploymentType, CrmDeploymentType.O365.ToString(), overrideDefaultSet: readLocalFirst); + SetServerConfigKey(config, Dynamics_ConfigFileServerKeys.CrmUseSSL, bool.FalseString, overrideDefaultSet: readLocalFirst); + SetServerConfigKey(config, Dynamics_ConfigFileServerKeys.AdvancedCheck, bool.FalseString, overrideDefaultSet: readLocalFirst); + SetServerConfigKey(config, Dynamics_ConfigFileServerKeys.Authority, overrideDefaultSet: readLocalFirst); + SetServerConfigKey(config, Dynamics_ConfigFileServerKeys.UserId, overrideDefaultSet: readLocalFirst); + + if (config.AppSettings.Settings[Dynamics_ConfigFileServerKeys.UseDefaultCreds.ToString()] != null) + { + SetServerConfigKey(config, Dynamics_ConfigFileServerKeys.UseDefaultCreds, overrideDefaultSet: readLocalFirst); + + if (!readLocalFirst) + { + if (StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.UseDefaultCreds) + .Equals("true", StringComparison.CurrentCultureIgnoreCase)) + { + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName, string.Empty); + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPassword, null); + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmDomain, string.Empty); + } + else + { + // Read domain from disc + SetServerConfigKey(config, Dynamics_ConfigFileServerKeys.CrmDomain, overrideDefaultSet: readLocalFirst); + + // Read from PW Vault + // Build HostName + StringBuilder credName = new StringBuilder(); + if (string.IsNullOrWhiteSpace(HostApplicatioNameOveride)) + credName.Append(AppDomain.CurrentDomain.FriendlyName.Replace(".vshost", "").Replace(".exe", "")); + else + credName.Append(HostApplicatioNameOveride.ToLowerInvariant().Replace(".vshost", "").Replace(".exe", "")); + + // Add ProfileName + if (UseUserLocalDirectoryForConfigStore) + credName.AppendFormat("_{0}", ProfileName); + + try + { + // Read Credentials from the Vault. + SavedCredentials creds = CredentialManager.ReadCredentials(credName.ToString()); + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName, creds.UserName); + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPassword, creds.Password); + } + catch (Win32Exception) + { + // Failed to read exception + // This is possible on a first read or where the vault has been cleared. logging set to Verbose so that it does not report an error in the logs under normal circumstances. + _tracer.Log("Failed to get credentials from Windows Vault", TraceEventType.Verbose); + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmUserName, string.Empty); + } + } + } + } + if (config.AppSettings.Settings[Dynamics_ConfigFileServerKeys.CacheCredentials.ToString()] != null) + { + // Get a switch to determine if we are in a non user caching mode. + // if set, then the user password is not cached and the system is set to "not use" default credentials. + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CacheCredentials, + config.AppSettings.Settings[Dynamics_ConfigFileServerKeys.CacheCredentials.ToString()].Value); + } + else + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CacheCredentials, true.ToString()); + + if (!readLocalFirst) + { + + // Handle the switch being set in configuration, outside of the UI. + bool cacheCreds = string.IsNullOrEmpty(StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CacheCredentials)) ? true : + StorageUtils.GetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CacheCredentials).Equals("true", StringComparison.CurrentCultureIgnoreCase); + if (!cacheCreds) + { + // Do not cache... + ForceFirstOAuthPrompt = true; + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmPassword, null); + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.CrmOrg, string.Empty); // Flush ORG so it can be reset. + } + } + + // Get query user for Org choice Setting.. defaults to off. + if (config.AppSettings.Settings[Dynamics_ConfigFileServerKeys.AskForOrg.ToString()] != null) + { + if (config.AppSettings.Settings[Dynamics_ConfigFileServerKeys.CrmPassword.ToString()] != null) + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.AskForOrg, + config.AppSettings.Settings[Dynamics_ConfigFileServerKeys.AskForOrg.ToString()].Value); + } + else + StorageUtils.SetConfigKey(ServerConfigKeys, Dynamics_ConfigFileServerKeys.AskForOrg, false.ToString()); + + } + } + _tracer.Log("LoadConfigFromFile()", TraceEventType.Stop); + return ServerConfigKeys; + } + catch (Exception ex) + { + _tracer.Log("LoadConfigFromFile() - Except - fail", TraceEventType.Error, ex); + _tracer.Log("LoadConfigFromFile() - Except - fail", TraceEventType.Stop); + return null; + } + } + + /// + /// Sets a key in the configuration + /// + /// Configuration File + /// Key to set + /// Default Value to set + /// If true, overrides default value logic. + private void SetServerConfigKey(Configuration config, Dynamics_ConfigFileServerKeys key, string defaultValue = null, bool overrideDefaultSet = false) + { + if (config.AppSettings.Settings[key.ToString()] != null) + { + StorageUtils.SetConfigKey(ServerConfigKeys, key, + config.AppSettings.Settings[key.ToString()].Value); + } + else + if (!string.IsNullOrEmpty(defaultValue)) + StorageUtils.SetConfigKey(ServerConfigKeys, key, defaultValue); + } + + /// + /// Save the configuration Keys to the configuration file. + /// + /// Config key dictionary + /// true on success + public bool SaveConfigToFile(Dictionary configToSave) + { + _tracer.Log("SaveConfigToFile()", TraceEventType.Start); + string ConfigPath = string.Empty; + try + { + if (configToSave != null) + { + // Get a handle to the configuration + if (UseUserLocalDirectoryForConfigStore) + { + string sPotentialPath = string.Empty; + if (string.IsNullOrWhiteSpace(HostApplicatioNameOveride)) + sPotentialPath = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft", + AppDomain.CurrentDomain.FriendlyName.Replace(".vshost", "").Replace(".exe", "")); + else + sPotentialPath = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft", + HostApplicatioNameOveride.ToLowerInvariant().Replace(".vshost", "").Replace(".exe", "")); + + + if (!Directory.Exists(sPotentialPath)) + { + Directory.CreateDirectory(sPotentialPath); + } + + // Set logging directory for errors. + ErrorLogger.LogfileDirectoryOverride = sPotentialPath; + + if (string.IsNullOrWhiteSpace(HostApplicatioNameOveride)) + ConfigPath = Path.Combine(sPotentialPath, string.Format("{0}_{1}", ProfileName, AppDomain.CurrentDomain.FriendlyName.Replace(".vshost", ""))); + else + ConfigPath = Path.Combine(sPotentialPath, string.Format("{0}_{1}", ProfileName, HostApplicatioNameOveride.Replace(".vshost", ""))); + } + else + ConfigPath = Environment.CommandLine.Replace("\"", "").Replace(".vshost", ""); + + + if (File.Exists(ConfigPath)) + { + Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigPath); + if (config != null) + { + // Clear the configuration data out.. + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.CrmDeploymentType.ToString()); + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.CrmUseSSL.ToString()); + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.CrmOrg.ToString()); + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.CrmPort.ToString()); + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.CrmServerName.ToString()); + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.UseDefaultCreds.ToString()); + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.CrmUserName.ToString()); + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.CrmPassword.ToString()); + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.CrmDomain.ToString()); + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.CacheCredentials.ToString()); + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.CrmOnlineRegion.ToString()); + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.AuthHomeRealm.ToString()); + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.AskForOrg.ToString()); + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.AdvancedCheck.ToString()); + + // Create new data. + config.AppSettings.Settings.Add(Dynamics_ConfigFileServerKeys.CrmDeploymentType.ToString(), StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.CrmDeploymentType)); + config.AppSettings.Settings.Add(Dynamics_ConfigFileServerKeys.CrmUseSSL.ToString(), StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.CrmUseSSL)); + config.AppSettings.Settings.Add(Dynamics_ConfigFileServerKeys.CrmOrg.ToString(), StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.CrmOrg)); + config.AppSettings.Settings.Add(Dynamics_ConfigFileServerKeys.CrmPort.ToString(), StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.CrmPort)); + config.AppSettings.Settings.Add(Dynamics_ConfigFileServerKeys.CrmServerName.ToString(), StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.CrmServerName)); + config.AppSettings.Settings.Add(Dynamics_ConfigFileServerKeys.UseDefaultCreds.ToString(), StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.UseDefaultCreds)); + config.AppSettings.Settings.Add(Dynamics_ConfigFileServerKeys.CacheCredentials.ToString(), StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.CacheCredentials)); + config.AppSettings.Settings.Add(Dynamics_ConfigFileServerKeys.CrmOnlineRegion.ToString(), StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.CrmOnlineRegion)); + config.AppSettings.Settings.Add(Dynamics_ConfigFileServerKeys.AuthHomeRealm.ToString(), StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.AuthHomeRealm)); + config.AppSettings.Settings.Add(Dynamics_ConfigFileServerKeys.AskForOrg.ToString(), StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.AskForOrg)); + config.AppSettings.Settings.Add(Dynamics_ConfigFileServerKeys.CrmDomain.ToString(), StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.CrmDomain)); + config.AppSettings.Settings.Add(Dynamics_ConfigFileServerKeys.AdvancedCheck.ToString(), StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.AdvancedCheck)); + + if (ServiceClient != null && ServiceClient.ActiveAuthenticationType == AuthenticationType.OAuth) + { + //stroring userid in config file after successful login + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.UserId.ToString()); + config.AppSettings.Settings.Add(Dynamics_ConfigFileServerKeys.UserId.ToString(), ServiceClient.OAuthUserId); + + if (!string.IsNullOrWhiteSpace(ServiceClient.Authority)) + { + //Storing authority in config file after validating the connection with OAuth + config.AppSettings.Settings.Remove(Dynamics_ConfigFileServerKeys.Authority.ToString()); + config.AppSettings.Settings.Add(Dynamics_ConfigFileServerKeys.Authority.ToString(), ServiceClient.Authority); + } + } + config.Save(ConfigurationSaveMode.Modified); // Save the changes. + ConfigurationManager.RefreshSection("appSettings"); // Force the configuration to Refresh the App Settings. + + // Only Run UID/PW process if Not using default PW / user account + if (!string.IsNullOrEmpty(StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.UseDefaultCreds)) && + !StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.UseDefaultCreds).Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase)) + { + // Use Windows Value to store passwords and such. + // Build HostName + StringBuilder credName = new StringBuilder(); + if (string.IsNullOrWhiteSpace(HostApplicatioNameOveride)) + credName.Append(AppDomain.CurrentDomain.FriendlyName.Replace(".vshost", "").Replace(".exe", "")); + else + credName.Append(HostApplicatioNameOveride.ToLowerInvariant().Replace(".vshost", "").Replace(".exe", "")); + + if (UseUserLocalDirectoryForConfigStore) // Add ProfileName if the UseUserLocalData is configured. + credName.AppendFormat("_{0}", ProfileName); + + string userName = StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.CrmUserName); + SecureString password = StorageUtils.GetConfigKey(configToSave, Dynamics_ConfigFileServerKeys.CrmPassword); + // Build credential array. + //OAuth: Skipping this when user login without entering credentials in UX. + if (!string.IsNullOrWhiteSpace(userName) && password != null) + { + SavedCredentials creds = new SavedCredentials(userName, password); + // Write creds to Vault. + try + { + CredentialManager.WriteCredentials(credName.ToString(), creds, true); + } + catch (Win32Exception ex) + { + // Failed to write exception + // This is possible if the windows vault is not present or the user does not have permissions to write to it. + _tracer.Log("Failed to write credentials to Windows Vault", TraceEventType.Verbose, ex); + } + } + else + { + try + { + CredentialManager.DeleteCredentials(credName.ToString(), false); + } + catch (Win32Exception ex) + { + // Failed to write exception + // This is possible if the windows vault is not present or the user does not have permissions to write to it. + _tracer.Log("Failed to remove unneeded credentials from Windows Vault", TraceEventType.Verbose, ex); + } + } + } + + if (UseUserLocalDirectoryForConfigStore) + { + try + { + // Encrypt the config file. + FileInfo fi = new FileInfo(config.FilePath); + fi.Encrypt(); + } + catch (IOException) + { + // This can happen when a certificate system on the host has failed. + // usually this can be fixed with the steps in this article : http://support.microsoft.com/kb/937536 + //tracer.Log("Failed to Encrypt Configuration File!", TraceEventType.Error, encrEX); + //tracer.Log("This problem may be related to a domain certificate in windows being out of sync with the domain, please read http://support.microsoft.com/kb/937536"); + } + catch (Exception) + { + //tracer.Log("Failed to Encrypt Configuration File!", TraceEventType.Error, genEX); + } + } + + _tracer.Log("SaveConfigToFile()", TraceEventType.Stop); + return true; + } + } + else + { + _tracer.Log(string.Format("SaveConfigToFile() - fail - cannot find file {0}", ConfigPath), TraceEventType.Error); + return false; + } + } + } + catch (Exception ex) + { + _tracer.Log("SaveConfigToFile() - fail - " + ConfigPath, + TraceEventType.Error, ex); + } + _tracer.Log("SaveConfigToFile() - fail", TraceEventType.Stop); + return false; + } + + #region IDisposable Support + + private bool disposedValue = false; // To detect redundant calls + + /// + /// Clean up + /// + /// + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + if (_onlineDiscoveryServerList != null) + { + _onlineDiscoveryServerList.Dispose(); + _onlineDiscoveryServerList = null; + } + + if (_tracer != null) + { + _tracer.Dispose(); + _tracer = null; + } + + //if (CrmSvc != null) + //{ + // CrmSvc.Dispose(); + // CrmSvc = null; + //} + } + disposedValue = true; + } + } + + + /// + /// Clean up + /// + public void Dispose() + { + Dispose(true); + } + #endregion + + #endregion + } + + /// + /// Event Raised when the Server Validate Connection Command operates. + /// + public class ServerConnectStatusEventArgs : EventArgs + { + /// + /// Error Message from the originating source + /// + public string ErrorMessage { get; private set; } + /// + /// if true, connected to CRM, else not connected to CRM + /// + public bool Connected { get; private set; } + /// + /// Exception that goes with the error message + /// + public Exception exEvent { get; private set; } + /// + /// Text status message, usually communicating the current state of the login process. + /// + public string StatusMessage { get; set; } + /// + /// If true, there were multiple organizations found, the user will need to select the correct org and re logon. + /// + public bool MultiOrgsFound { get; private set; } + + /// + public ServerConnectStatusEventArgs(string statusMsg) + { + StatusMessage = statusMsg; + } + + /// + public ServerConnectStatusEventArgs(string errMsg, bool connected) + { + ErrorMessage = errMsg; + Connected = connected; + exEvent = null; + } + + /// + public ServerConnectStatusEventArgs(string errMsg, bool connected, bool multiOrgFound) + { + ErrorMessage = errMsg; + Connected = connected; + exEvent = null; + MultiOrgsFound = multiOrgFound; + } + + + /// + public ServerConnectStatusEventArgs(string errMsg, bool connected, Exception except) + { + ErrorMessage = errMsg; + Connected = connected; + exEvent = except; + } + + /// + public ServerConnectStatusEventArgs() + { + + } + } + +} diff --git a/src/GeneralTools/DataverseClient/ConnectControl/Doc/AppConfig_Settings_for_LoginControl.docx b/src/GeneralTools/DataverseClient/ConnectControl/Doc/AppConfig_Settings_for_LoginControl.docx new file mode 100644 index 0000000..f668bad Binary files /dev/null and b/src/GeneralTools/DataverseClient/ConnectControl/Doc/AppConfig_Settings_for_LoginControl.docx differ diff --git a/src/GeneralTools/DataverseClient/ConnectControl/ErrorWindow.xaml b/src/GeneralTools/DataverseClient/ConnectControl/ErrorWindow.xaml new file mode 100644 index 0000000..46d9582 --- /dev/null +++ b/src/GeneralTools/DataverseClient/ConnectControl/ErrorWindow.xaml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +