Skip to content

07102024 release - Updates to RequestBuilder Shipping Connector #458

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/GeneralTools/DataverseClient/Client/Auth/AuthProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ internal async static Task<ExecuteAuthenticationResults> ExecuteAuthenticateServ
createdLogSource = true;
logSink = new DataverseTraceLogger();
}

// Set the logger in the MSAL Logger
MSALLoggerCallBack mSALLogger = new MSALLoggerCallBack(logSink);

string Authority = string.Empty;
string Resource = string.Empty;
Expand Down Expand Up @@ -139,7 +142,7 @@ internal async static Task<ExecuteAuthenticationResults> ExecuteAuthenticateServ
.WithAuthority(Authority)
.WithLegacyCacheCompatibility(false)
.WithHttpClientFactory(new MSALHttpClientFactory())
.WithLogging(MSALLoggerCallBack.Log);
.WithLogging(mSALLogger.Log);
}

// initialization of memory cache if its not already initialized.
Expand Down Expand Up @@ -189,7 +192,7 @@ internal async static Task<ExecuteAuthenticationResults> ExecuteAuthenticateServ
})
.WithAuthority(Authority)
.WithLegacyCacheCompatibility(false)
.WithLogging(MSALLoggerCallBack.Log);
.WithLogging(mSALLogger.Log);

pApp = cApp.Build();

Expand Down Expand Up @@ -300,9 +303,7 @@ internal async static Task<AuthenticationResult> ObtainAccessTokenAsync(
}
else
{
#pragma warning disable CS0618 // Type or member is obsolete
_authenticationResult = await publicAppClient.AcquireTokenByUsernamePassword(scopes, clientCredentials.UserName.UserName, ServiceClient.MakeSecureString(clientCredentials.UserName.Password)).ExecuteAsync().ConfigureAwait(false);
#pragma warning restore CS0618 // Type or member is obsolete
_authenticationResult = await publicAppClient.AcquireTokenByUsernamePassword(scopes, clientCredentials.UserName.UserName, clientCredentials.UserName.Password).ExecuteAsync().ConfigureAwait(false);
}
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ internal OrganizationRequest BuildRequest(OrganizationRequest request)
parameters.Add(RequestBinderUtil.HEADERLIST, new Dictionary<string,string>(_headers));
}

if (_crmUserId != null && _crmUserId != Guid.Empty)
{
parameters.Add(RequestHeaders.CALLER_OBJECT_ID_HTTP_HEADER, _crmUserId.Value);
}

if (_aadOidId != null && _aadOidId != Guid.Empty)
{
parameters.Add(RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER, _aadOidId.Value);
}

request.Parameters.AddRange(parameters);

// Clear in case this is reused.
Expand Down
18 changes: 7 additions & 11 deletions src/GeneralTools/DataverseClient/Client/ConnectionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ internal bool CalledbyExecuteRequest
/// <summary>
/// Logging provider for DataverseConnectionServiceobject.
/// </summary>
private DataverseTraceLogger logEntry { get; set; }
internal DataverseTraceLogger logEntry { get; set; }

/// <summary>
/// Returns Logs from this process.
Expand Down Expand Up @@ -2472,18 +2472,14 @@ private bool ShouldRetryWebAPI(Exception ex, int retryCount, int maxRetryCount,
errorCode == ((int)ErrorCodes.ThrottlingTimeExceededError).ToString() ||
errorCode == ((int)ErrorCodes.ThrottlingConcurrencyLimitExceededError).ToString())
{
if (errorCode == ((int)ErrorCodes.ThrottlingBurstRequestLimitExceededError).ToString())
{
// Use Retry-After delay when specified
if (httpOperationException.Response.Headers.ContainsKey("Retry-After"))
_retryPauseTimeRunning = TimeSpan.Parse(httpOperationException.Response.Headers["Retry-After"].FirstOrDefault());
else
_retryPauseTimeRunning = retryPauseTime.Add(TimeSpan.FromSeconds(Math.Pow(2, retryCount))); ; // default timespan with back off is response does not contain the tag..
if (httpOperationException.Response.Headers.TryGetValue("Retry-After", out var retryAfter) && double.TryParse(retryAfter.FirstOrDefault(), out var retrySeconds))
{
// Note: Retry-After header is in seconds.
_retryPauseTimeRunning = TimeSpan.FromSeconds(retrySeconds);
}
else
{
// else use exponential back off delay
_retryPauseTimeRunning = retryPauseTime.Add(TimeSpan.FromSeconds(Math.Pow(2, retryCount)));
{
_retryPauseTimeRunning = retryPauseTime.Add(TimeSpan.FromSeconds(Math.Pow(2, retryCount))); ; // default timespan with back off is response does not contain the tag..
}
isThrottlingRetry = true;
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public DataverseTelemetryBehaviors(ConnectionService cli)

// reading overrides from app config if present..
// these values override the values that are set on the client from the server.
DataverseTraceLogger logg = new DataverseTraceLogger();
DataverseTraceLogger logg = _callerCdsConnectionServiceHandler.logEntry;
try
{
// Initialize user agent
Expand Down Expand Up @@ -107,22 +107,18 @@ public DataverseTelemetryBehaviors(ConnectionService cli)
if (_maxBufferPoolSize < MAXBUFFERPOOLDEFAULT)
{
_maxBufferPoolSize = -1;
logg.Log($"Failed to set MaxBufferPoolSizeOveride property. Value found: {maxBufferPoolSz}. Size must be larger then {MAXBUFFERPOOLDEFAULT}.", System.Diagnostics.TraceEventType.Warning);
logg.Log($"Failed to set MaxBufferPoolSizeOverride property. Value found: {maxBufferPoolSz}. Size must be larger then {MAXBUFFERPOOLDEFAULT}.", System.Diagnostics.TraceEventType.Warning);
}
}
}
else
logg.Log($"Failed to parse MaxBufferPoolSizeOveride property. Value found: {maxBufferPoolSz}. MaxReceivedMessageSizeOverride must be a valid integer.", System.Diagnostics.TraceEventType.Warning);
logg.Log($"Failed to parse MaxBufferPoolSizeOverride property. Value found: {maxBufferPoolSz}. MaxReceivedMessageSizeOverride must be a valid integer.", System.Diagnostics.TraceEventType.Warning);
}

}
catch (Exception ex)
{
logg.Log("Failed to process binding override properties, Only MaxFaultSizeOverride, MaxReceivedMessageSizeOverride and MaxBufferPoolSizeOveride are supported and must be integers.", System.Diagnostics.TraceEventType.Warning, ex);
}
finally
{
logg.Dispose();
logg.Log("Failed to process binding override properties, Only MaxFaultSizeOverride, MaxReceivedMessageSizeOverride and MaxBufferPoolSizeOverride are supported and must be integers.", System.Diagnostics.TraceEventType.Warning, ex);
}
}

Expand Down Expand Up @@ -258,6 +254,8 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel)
httpRequestMessage = new HttpRequestMessageProperty();
}

string[] CrmUserIdList = null;
string[] AADOidList = null;
if (httpRequestMessage != null)
{
httpRequestMessage.Headers[Utilities.RequestHeaders.USER_AGENT_HTTP_HEADER] = _userAgent;
Expand All @@ -282,27 +280,68 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel)
Utilities.CleanUpHeaderKeys(httpRequestMessage.Headers);
if (httpRequestMessageObject == null)
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);

CrmUserIdList = httpRequestMessage.Headers.GetValues(Utilities.RequestHeaders.CALLER_OBJECT_ID_HTTP_HEADER);
AADOidList = httpRequestMessage.Headers.GetValues(Utilities.RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER);

}

// Adding SOAP headers
Guid callerId = Guid.Empty;
if (_callerCdsConnectionServiceHandler != null)
Guid AADOId = Guid.Empty;
if (CrmUserIdList != null && CrmUserIdList.Length > 0)
{
if(!Guid.TryParse(CrmUserIdList[0], out callerId))
_callerCdsConnectionServiceHandler.logEntry.Log("Failed to parse Caller Object ID from the HTTP Header.", System.Diagnostics.TraceEventType.Warning);
CrmUserIdList = null; // Clear the list.
}

if (AADOidList != null && AADOidList.Length > 0)
{
if (_callerCdsConnectionServiceHandler.WebClient != null)
callerId = _callerCdsConnectionServiceHandler.WebClient.CallerId;
if (_callerCdsConnectionServiceHandler.OnPremClient != null)
callerId = _callerCdsConnectionServiceHandler.OnPremClient.CallerId;
if (!Guid.TryParse(AADOidList[0], out AADOId))
_callerCdsConnectionServiceHandler.logEntry.Log("Failed to parse AADObjectID from the HTTP Header.", System.Diagnostics.TraceEventType.Warning);
AADOidList = null; // Clear the list.
}

if (callerId == Guid.Empty) // Prefer the Caller ID over the AADObjectID.
if (callerId == Guid.Empty && AADOId == Guid.Empty)
{
if (_callerCdsConnectionServiceHandler != null)
{
if (_callerCdsConnectionServiceHandler.WebClient != null)
callerId = _callerCdsConnectionServiceHandler.WebClient.CallerId;
if (_callerCdsConnectionServiceHandler.OnPremClient != null)
callerId = _callerCdsConnectionServiceHandler.OnPremClient.CallerId;
}

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 Envelope.
// Set a header request with the AAD Caller Object ID.
using (OperationContextScope scope = new OperationContextScope((IContextChannel)channel))
{
var AADCallerIdHeader = new MessageHeader<Guid>(_callerCdsConnectionServiceHandler.CallerAADObjectId.Value).GetUntypedHeader(Utilities.RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER, "http://schemas.microsoft.com/xrm/2011/Contracts");
request.Headers.Add(AADCallerIdHeader);
}
}
}
}
else
{
if (_callerCdsConnectionServiceHandler != null && (_callerCdsConnectionServiceHandler.CallerAADObjectId.HasValue && _callerCdsConnectionServiceHandler.CallerAADObjectId.Value != Guid.Empty))
if ( callerId != Guid.Empty )
{
using (OperationContextScope scope = new OperationContextScope((IContextChannel)channel))
{
var CallerIdHeader = new MessageHeader<Guid>(callerId).GetUntypedHeader(Xrm.Sdk.Client.SdkHeaders.CallerId, Xrm.Sdk.XmlNamespaces.V5.Contracts);
request.Headers.Add(CallerIdHeader);
}
}
else if (AADOId != Guid.Empty)
{
// Add Caller ID to the SOAP Envelope.
// Set a header request with the AAD Caller Object ID.
using (OperationContextScope scope = new OperationContextScope((IContextChannel)channel))
{
var AADCallerIdHeader = new MessageHeader<Guid>(_callerCdsConnectionServiceHandler.CallerAADObjectId.Value).GetUntypedHeader(Utilities.RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER, "http://schemas.microsoft.com/xrm/2011/Contracts");
var AADCallerIdHeader = new MessageHeader<Guid>(AADOId).GetUntypedHeader(Utilities.RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER, "http://schemas.microsoft.com/xrm/2011/Contracts");
request.Headers.Add(AADCallerIdHeader);
}
}
Expand Down
9 changes: 4 additions & 5 deletions src/GeneralTools/DataverseClient/Client/ServiceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2039,11 +2039,10 @@ private bool ShouldRetry(OrganizationRequest req, Exception ex, int retryCount,
OrgEx.Detail.ErrorCode == ErrorCodes.ThrottlingTimeExceededError ||
OrgEx.Detail.ErrorCode == ErrorCodes.ThrottlingConcurrencyLimitExceededError)
{
// Error was raised by a instance throttle trigger.
if (OrgEx.Detail.ErrorCode == ErrorCodes.ThrottlingBurstRequestLimitExceededError)
{
// Use Retry-After delay when specified
_retryPauseTimeRunning = (TimeSpan)OrgEx.Detail.ErrorDetails["Retry-After"];
// Use Retry-After delay when specified
if (OrgEx.Detail.ErrorDetails.TryGetValue("Retry-After", out var retryAfter) && retryAfter is TimeSpan retryAsTimeSpan)
{
_retryPauseTimeRunning = retryAsTimeSpan;
}
else
{
Expand Down
40 changes: 28 additions & 12 deletions src/GeneralTools/DataverseClient/Client/Utils/MSALLoggerCallBack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,42 @@ namespace Microsoft.PowerPlatform.Dataverse.Client.Utils
/// <summary>
/// This class will be used to support hooking into MSAL Call back logic.
/// </summary>
internal static class MSALLoggerCallBack
internal class MSALLoggerCallBack
{
private static DataverseTraceLogger _logEntry;

public DataverseTraceLogger LogSink { get; set; } = null;

/// <summary>
/// Enabled PII logging for this connection.
/// if this flag is set, it will override the value from app config.
/// </summary>
public static bool? EnabledPIILogging { get; set; } = null;
public bool? EnabledPIILogging { get; set; } = null;

public MSALLoggerCallBack(DataverseTraceLogger logSink = null, bool? enabledPIILogging = null)
{
LogSink = logSink;
EnabledPIILogging = enabledPIILogging;
}

/// <summary>
///
/// </summary>
/// <param name="level"></param>
/// <param name="message"></param>
/// <param name="containsPii"></param>
static public void Log(LogLevel level, string message, bool containsPii)
public void Log(LogLevel level, string message, bool containsPii)
{
if (_logEntry == null)
_logEntry = new DataverseTraceLogger(typeof(LogCallback).Assembly.GetName().Name); // set up logging client.
bool createdLogSource = false;
if (LogSink == null)
{
createdLogSource = true;
LogSink = new DataverseTraceLogger(typeof(LogCallback).Assembly.GetName().Name); // set up logging client.
}

if (!EnabledPIILogging.HasValue)
{
EnabledPIILogging = ClientServiceProviders.Instance.GetService<IOptions<ConfigurationOptions>>().Value.MSALEnabledLogPII;
_logEntry.Log($"Setting MSAL PII Logging Feature to {EnabledPIILogging.Value}", System.Diagnostics.TraceEventType.Information);
LogSink.Log($"Setting MSAL PII Logging Feature to {EnabledPIILogging.Value}", System.Diagnostics.TraceEventType.Information);
}

if (containsPii && !EnabledPIILogging.Value)
Expand All @@ -41,25 +52,30 @@ static public void Log(LogLevel level, string message, bool containsPii)
}

// Add (PII) prefix to messages that have PII in them per AAD Message alert.
message = containsPii ? $"(PII){message}" : message;
message = containsPii ? $"(PII){message}" : message;

switch (level)
{
case LogLevel.Info:
_logEntry.Log(message, System.Diagnostics.TraceEventType.Information);
LogSink.Log(message, System.Diagnostics.TraceEventType.Information);
break;
case LogLevel.Verbose:
_logEntry.Log(message, System.Diagnostics.TraceEventType.Verbose);
LogSink.Log(message, System.Diagnostics.TraceEventType.Verbose);
break;
case LogLevel.Warning:
_logEntry.Log(message, System.Diagnostics.TraceEventType.Warning);
LogSink.Log(message, System.Diagnostics.TraceEventType.Warning);
break;
case LogLevel.Error:
_logEntry.Log(message, System.Diagnostics.TraceEventType.Error);
LogSink.Log(message, System.Diagnostics.TraceEventType.Error);
break;
default:
break;
}

if (createdLogSource)
{
LogSink.Dispose();
}
}

}
Expand Down
12 changes: 12 additions & 0 deletions src/GeneralTools/DataverseClient/Client/Utils/RequestBinderUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,23 @@ internal static void ProcessRequestBinderProperties(HttpRequestMessageProperty h
}
continue;
}
if (itm.Key == Utilities.RequestHeaders.CALLER_OBJECT_ID_HTTP_HEADER)
{
AddorUpdateHeaderProperties(httpRequestMessageHeaders, Utilities.RequestHeaders.CALLER_OBJECT_ID_HTTP_HEADER, itm.Value.ToString());
continue;
}
if (itm.Key == Utilities.RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER)
{
AddorUpdateHeaderProperties(httpRequestMessageHeaders, Utilities.RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER, itm.Value.ToString());
continue;
}
}
if ( request.Parameters.Count > 0 )
{
request.Parameters.Remove(Utilities.RequestHeaders.X_MS_CORRELATION_REQUEST_ID);
request.Parameters.Remove(Utilities.RequestHeaders.X_MS_CLIENT_SESSION_ID);
request.Parameters.Remove(Utilities.RequestHeaders.CALLER_OBJECT_ID_HTTP_HEADER);
request.Parameters.Remove(Utilities.RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER);
request.Parameters.Remove(HEADERLIST);
}
}
Expand Down
Loading