/// <summary> /// Reads app setting from config file; if not found, returns <param name="defaultValue">the default value</param>. /// </summary> /// <param name="key">The setting key</param> /// <param name="logSink">Logger to use if available, else will be created and used for this session only</param> /// <return>Returns the setting value or default value.</return> public static T GetAppSetting <T>(string key, T defaultValue, CdsTraceLogger logSink = null) { bool isLogEntryCreatedLocaly = false; try { var value = System.Configuration.ConfigurationManager.AppSettings[key]; if (value == null) { return(defaultValue); } return(ConvertFromInvariantString <T>(value)); } catch (Exception ex) { if (logSink == null) // building on first use to optimize { logSink = new CdsTraceLogger(); isLogEntryCreatedLocaly = true; } logSink.Log($"Failed to read {key} from AppSettings, failed with message: {ex.Message}. Using default value", System.Diagnostics.TraceEventType.Warning); return(defaultValue); } finally { if (isLogEntryCreatedLocaly) { logSink.Dispose(); } } }
/// <summary> /// Picks up a string value from the app config and converts it to the string value version based on the format provided. /// </summary> /// <param name="key">Key to lookup from the appconfig</param> /// <param name="format">TimeSpan format to convert from ( "d" = days, "m" = Minuets , "h" = hours , "s" = seconds , "ms" = Milliseconds ) </param> /// <param name="defaultValue">default value to use of the key is not found</param> /// <param name="logSink">Logger to use if available, else will be created and used for this session only</param> /// <returns>Determined timeSpan value.</returns> public static TimeSpan GetAppSettingTimeSpan(string key, TimeSpanFromKey format, TimeSpan defaultValue, CdsTraceLogger logSink = null) { bool isLogEntryCreatedLocaly = false; try { var value = System.Configuration.ConfigurationManager.AppSettings[key]; if (value == null) { return(defaultValue); } double incomingValue; if (!double.TryParse(value, out incomingValue)) { if (logSink == null) // building on first use to optimize { logSink = new CdsTraceLogger(); isLogEntryCreatedLocaly = true; } logSink.Log($"Failed to read {key} from AppSettings, Value {incomingValue} cannot be parsed to a double. Using default value", System.Diagnostics.TraceEventType.Warning); return(defaultValue); } switch (format) { case TimeSpanFromKey.Minutes: return(TimeSpan.FromMinutes(incomingValue)); case TimeSpanFromKey.Hours: return(TimeSpan.FromHours(incomingValue)); case TimeSpanFromKey.Seconds: return(TimeSpan.FromSeconds(incomingValue)); case TimeSpanFromKey.Milliseconds: return(TimeSpan.FromMilliseconds(incomingValue)); case TimeSpanFromKey.Days: return(TimeSpan.FromDays(incomingValue)); default: return(defaultValue); } } finally { if (isLogEntryCreatedLocaly) { logSink.Dispose(); } } }
/// <summary> /// Acquires Confidential client token. /// </summary> /// <param name="confidentialAppClient">Confidential client application</param> /// <param name="scopes">Scope List</param> /// <param name="logSink">Logger to use</param> /// <returns>Authentication Result with updated token</returns> internal async static Task <AuthenticationResult> ObtainAccessTokenAsync( IConfidentialClientApplication confidentialAppClient, List <string> scopes, CdsTraceLogger logSink = null) { // This works for user Auth flows. AuthenticationResult _authenticationResult = null; if (confidentialAppClient != null) { _authenticationResult = await confidentialAppClient.AcquireTokenForClient(scopes).ExecuteAsync(); } else { // throw here. } return(_authenticationResult); }
/// <summary> /// /// </summary> /// <param name="level"></param> /// <param name="message"></param> /// <param name="containsPii"></param> static public void Log(LogLevel level, string message, bool containsPii) { if (_logEntry == null) { _logEntry = new CdsTraceLogger("Microsoft.IdentityModel.Clients.ActiveDirectory"); // set up logging client. } if (!EnabledPIILogging.HasValue) { EnabledPIILogging = Utils.AppSettingsHelper.GetAppSetting("LogADALPII", false); LoggerCallbackHandler.PiiLoggingEnabled = EnabledPIILogging.Value; _logEntry.Log($"Setting ADAL PII Logging Feature to {EnabledPIILogging.Value}", System.Diagnostics.TraceEventType.Information); } if (containsPii && !EnabledPIILogging.Value) { _logEntry.Log($"ADAL LOG EVENT SKIPPED --> PII Logging Disabled.", System.Diagnostics.TraceEventType.Warning); return; } // Add (PII) prefix to messages that have PII in them per AAD Message alert. message = containsPii ? $"(PII){message}" : message; switch (level) { case LogLevel.Information: _logEntry.Log(message, System.Diagnostics.TraceEventType.Information); break; case LogLevel.Verbose: _logEntry.Log(message, System.Diagnostics.TraceEventType.Verbose); break; case LogLevel.Warning: _logEntry.Log(message, System.Diagnostics.TraceEventType.Warning); break; case LogLevel.Error: _logEntry.Log(message, System.Diagnostics.TraceEventType.Error); break; default: break; } }
/// <summary> /// Creates a CdsService Client Exception from a httpOperationResult. /// </summary> /// <param name="httpOperationException"></param> public static CdsClientOperationException GenerateCdsClientOperationException(HttpOperationException httpOperationException) { string errorDetailPrefixString = "@Microsoft.PowerApps.CDS.ErrorDetails."; Dictionary <string, string> cdsErrorData = new Dictionary <string, string>(); JObject contentBody = JObject.Parse(httpOperationException.Response.Content); var ErrorBlock = contentBody["error"]; var errorMessage = CdsTraceLogger.GetFirstLineFromString(ErrorBlock["message"].ToString()).Trim(); int HResult = Convert.ToInt32(ErrorBlock["code"].ToString(), 16); string HelpLink = ErrorBlock["@Microsoft.PowerApps.CDS.HelpLink"].ToString(); foreach (var node in ErrorBlock.ToArray()) { if (node.Path.Contains(errorDetailPrefixString)) { cdsErrorData.Add(node.Value <JProperty>().Name.ToString().Replace(errorDetailPrefixString, string.Empty), node.HasValues? node.Value <JProperty>().Value.ToString() : string.Empty); } } return(new CdsClientOperationException(errorMessage, HResult, HelpLink, cdsErrorData)); }
/// <summary> /// Process ADAL exception and provide common handlers. /// </summary> /// <param name="serviceUrl"></param> /// <param name="clientCredentials"></param> /// <param name="userCert"></param> /// <param name="clientId"></param> /// <param name="redirectUri"></param> /// <param name="promptBehavior"></param> /// <param name="isOnPrem"></param> /// <param name="authority"></param> /// <param name="logSink"></param> /// <param name="useDefaultCreds"></param> /// <param name="adalEx"></param> /// <param name="msalAuthClient"></param> private async static Task <ExecuteAuthenticationResults> ProcessAdalExecptionAsync(Uri serviceUrl, ClientCredentials clientCredentials, X509Certificate2 userCert, string clientId, Uri redirectUri, PromptBehavior promptBehavior, bool isOnPrem, string authority, object msalAuthClient, CdsTraceLogger 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)); } 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; } }
/// <summary> /// Get authority and resource for this instance. /// </summary> /// <param name="targetServiceUrl">URI to query</param> /// <param name="logger">Logger to write info too</param> /// <param name="clientFactory">HTTP Client factory to use for this request.</param> /// <returns></returns> private static async Task <AuthRoutingProperties> GetAuthorityFromTargetServiceAsync(IHttpClientFactory clientFactory, Uri targetServiceUrl, CdsTraceLogger logger) { AuthRoutingProperties authRoutingProperties = new AuthRoutingProperties(); var client = clientFactory.CreateClient("CdsHttpClientFactory"); 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")) { 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 <string, string> 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); }
/// <summary> /// Executes Authentication against a service /// </summary> /// <param name="serviceUrl"></param> /// <param name="clientCredentials"></param> /// <param name="user"></param> /// <param name="clientId"></param> /// <param name="redirectUri"></param> /// <param name="promptBehavior"></param> /// <param name="isOnPrem"></param> /// <param name="authority"></param> /// <param name="userCert">Certificate of provided to login with</param> /// <param name="logSink">(optional) Initialized CdsTraceLogger Object</param> /// <param name="useDefaultCreds">(optional) if set, tries to login as the current user.</param> /// <param name="msalAuthClient">Object of either confidential or public client</param> /// <param name="clientSecret"></param> /// <param name="addVersionInfoToUri">indicates if the serviceURI should be updated to include the /web?sdk version</param> /// <returns>AuthenticationResult containing a JWT Token for the requested Resource and user/app</returns> internal async static Task <ExecuteAuthenticationResults> ExecuteAuthenticateServiceProcessAsync( Uri serviceUrl, ClientCredentials clientCredentials, X509Certificate2 userCert, string clientId, Uri redirectUri, PromptBehavior promptBehavior, bool isOnPrem, string authority, object msalAuthClient, CdsTraceLogger 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 CdsTraceLogger(); } 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 <IHttpClientFactory>(), processResult.TargetServiceUrl, logSink).ConfigureAwait(false).GetAwaiter().GetResult(); if (!string.IsNullOrEmpty(rslt.Authority)) { 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 // Assign outbound properties. processResult.Resource = Resource; processResult.Authority = Authority; logSink.Log("AuthenticateService - found authority with name " + (string.IsNullOrEmpty(Authority) ? "<Not Provided>" : Authority)); logSink.Log("AuthenticateService - found resource with name " + (string.IsNullOrEmpty(Resource) ? "<Not Provided>" : Resource)); Uri ResourceUri = new Uri(Resource); // Add Scope, List <string> 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.Cds.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); } else { if (clientSecret != null) { logSink.Log("Initial ObtainAccessToken - Client Secret", TraceEventType.Verbose); cApp = cAppBuilder.WithClientSecret(clientSecret.ToUnsecureString()).Build(); _authenticationResult = await ObtainAccessTokenAsync(cApp, Scopes, logSink); } 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.Cds.Client.Utils.ADALLoggerCallBack.Log); pApp = cApp.Build(); } //Run user Auth flow. _authenticationResult = await ObtainAccessTokenAsync(pApp, Scopes, user, promptBehavior, clientCredentials, useDefaultCreds, logSink); // 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); 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); }
/// <summary> /// Token refresh flow for MSAL User Flows. /// </summary> /// <param name="publicAppClient">MSAL Client to use.</param> /// <param name="scopes">Scopes to send in.</param> /// <param name="account"></param> /// <param name="promptBehavior">prompting behavior</param> /// <param name="clientCredentials">user credential package</param> /// <param name="useDefaultCreds">should system default creds be used</param> /// <param name="logSink">logger to write logs too.</param> /// <returns></returns> internal async static Task <AuthenticationResult> ObtainAccessTokenAsync( IPublicClientApplication publicAppClient, List <string> scopes, IAccount account, PromptBehavior promptBehavior, ClientCredentials clientCredentials, bool useDefaultCreds = false, CdsTraceLogger 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)) { _authenticationResult = publicAppClient.AcquireTokenByUsernamePassword(scopes, clientCredentials.UserName.UserName, CdsServiceClient.MakeSecureString(clientCredentials.UserName.Password)).ExecuteAsync().Result; } else { if (useDefaultCreds) { if (!string.IsNullOrEmpty(loginUserHint)) { _authenticationResult = await publicAppClient.AcquireTokenByIntegratedWindowsAuth(scopes).WithUsername(loginUserHint).ExecuteAsync(); } else { _authenticationResult = await publicAppClient.AcquireTokenByIntegratedWindowsAuth(scopes).ExecuteAsync(); } } 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(); } else { if (account != null) { _authenticationResult = await publicAppClient.AcquireTokenSilent(scopes, account).ExecuteAsync(); } else { _authenticationResult = await publicAppClient.AcquireTokenInteractive(scopes).WithLoginHint(loginUserHint).ExecuteAsync(); } } } } } else { // throw here. } return(_authenticationResult); }
/// <summary> /// Default constructor, Builds baseline data for the Servers. /// </summary> public CdsDiscoveryServers() { if (logger == null) { logger = new CdsTraceLogger(); } if (_OSDPServers == null) { _OSDPServers = new ObservableCollection <CdsDiscoveryServer>(); } _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = null, DisplayName = "Don’t Know", ShortName = "" }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm5.dynamics.com/XRMServices/2011/Discovery.svc"), DisplayName = "Asia Pacific Area", ShortName = "APAC", GeoCode = "APAC" }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm3.dynamics.com/XRMServices/2011/Discovery.svc"), DisplayName = "Canada", ShortName = "CAN", GeoCode = "CAN" }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm.dynamics.cn/XRMServices/2011/Discovery.svc"), DisplayName = "China", ShortName = "CHN", GeoCode = "CHN", RequiresRegionalDiscovery = true, RegionalGlobalDiscoveryServer = new Uri("https://globaldisco.crm.dynamics.cn") }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm4.dynamics.com/XRMServices/2011/Discovery.svc"), DisplayName = "Europe, Middle East and Africa", ShortName = "EMEA", GeoCode = "EMEA" }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm12.dynamics.com/XRMServices/2011/Discovery.svc"), DisplayName = "France", ShortName = "FRA", GeoCode = "FRA" }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm16.dynamics.com/XRMServices/2011/Discovery.svc"), DisplayName = "Germany (Go Local)", ShortName = "GER", GeoCode = "GER" }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm.Microsoftdynamics.de/XRMServices/2011/Discovery.svc"), DisplayName = "Germany", ShortName = "DEU", RequiresRegionalDiscovery = true, GeoCode = "DEU" }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm8.dynamics.com/XRMServices/2011/Discovery.svc"), DisplayName = "India", ShortName = "IND", GeoCode = "IND" }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm7.dynamics.com/XRMServices/2011/Discovery.svc"), DisplayName = "Japan", ShortName = "JPN", GeoCode = "JPN" }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm.dynamics.com/XRMServices/2011/Discovery.svc"), DisplayName = "North America", ShortName = "NorthAmerica" }); // Do not add Geo code to NAM or GCC, as they use the same server level GEO code. _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm9.dynamics.com/XRMServices/2011/Discovery.svc"), DisplayName = "North America 2", ShortName = "NorthAmerica2", RequiresRegionalDiscovery = true, RegionalGlobalDiscoveryServer = new Uri("https://globaldisco.crm9.dynamics.com") }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm6.dynamics.com/XRMServices/2011/Discovery.svc"), DisplayName = "Oceania", ShortName = "Oceania", GeoCode = "OCE" }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm14.dynamics.com/XRMServices/2011/Discovery.svc"), DisplayName = "South Africa", ShortName = "ZAF", GeoCode = "ZAF" }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm2.dynamics.com/XRMServices/2011/Discovery.svc"), DisplayName = "South America", ShortName = "SouthAmerica", GeoCode = "LATAM" }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm15.dynamics.com/XRMServices/2011/Discovery.svc"), DisplayName = "United Arab Emirates", ShortName = "UAE", GeoCode = "UAE" }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm11.dynamics.com/XRMServices/2011/Discovery.svc"), DisplayName = "United Kingdom", ShortName = "GBR", GeoCode = "GBR" }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm.appsplatform.us/XRMServices/2011/Discovery.svc"), DisplayName = "US Gov DoD", ShortName = "DoD", GeoCode = "DOD", RequiresRegionalDiscovery = true, RegionalGlobalDiscoveryServer = new Uri("https://globaldisco.crm.appsplatform.us") }); _OSDPServers.Add(new CdsDiscoveryServer() { DiscoveryServer = new Uri("https://disco.crm.microsoftdynamics.us/XRMServices/2011/Discovery.svc"), DisplayName = "US Gov High", ShortName = "USG", GeoCode = "USG", RequiresRegionalDiscovery = true, RegionalGlobalDiscoveryServer = new Uri("https://globaldisco.crm.microsoftdynamics.us") }); #if DEBUG var internalEnvInfo = new InternalEnvInfo(); _OSDPServers.Add(new CdsDiscoveryServer(internalEnvInfo.CrmLiveTieOSDP)); _OSDPServers.Add(new CdsDiscoveryServer(internalEnvInfo.CrmIntOSDP)); _OSDPServers.Add(new CdsDiscoveryServer(internalEnvInfo.CrmLiveTodayDebugOSDP)); _OSDPServers.Add(new CdsDiscoveryServer(internalEnvInfo.CrmLiveTodayDebugLIVE)); _OSDPServers.Add(new CdsDiscoveryServer(internalEnvInfo.Crm1BoxTest)); _OSDPServers.Add(new CdsDiscoveryServer(internalEnvInfo.CrmTIP)); _OSDPServers.Add(new CdsDiscoveryServer(internalEnvInfo.Crm2LiveTie)); #endif }