Skip to content

Commit

Permalink
Merge pull request #458 from microsoft/release/update/240710062439
Browse files Browse the repository at this point in the history
07102024 release - Updates to RequestBuilder Shipping Connector
  • Loading branch information
MattB-msft committed Jul 10, 2024
2 parents d78a53d + 440faf1 commit 0bf4724
Show file tree
Hide file tree
Showing 20 changed files with 632 additions and 151 deletions.
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

0 comments on commit 0bf4724

Please sign in to comment.