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