Skip to content

Commit 0bf4724

Browse files
authored
Merge pull request #458 from microsoft/release/update/240710062439
07102024 release - Updates to RequestBuilder Shipping Connector
2 parents d78a53d + 440faf1 commit 0bf4724

20 files changed

+632
-151
lines changed

src/GeneralTools/DataverseClient/Client/Auth/AuthProcessor.cs

+6-5
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ internal async static Task<ExecuteAuthenticationResults> ExecuteAuthenticateServ
7070
createdLogSource = true;
7171
logSink = new DataverseTraceLogger();
7272
}
73+
74+
// Set the logger in the MSAL Logger
75+
MSALLoggerCallBack mSALLogger = new MSALLoggerCallBack(logSink);
7376

7477
string Authority = string.Empty;
7578
string Resource = string.Empty;
@@ -139,7 +142,7 @@ internal async static Task<ExecuteAuthenticationResults> ExecuteAuthenticateServ
139142
.WithAuthority(Authority)
140143
.WithLegacyCacheCompatibility(false)
141144
.WithHttpClientFactory(new MSALHttpClientFactory())
142-
.WithLogging(MSALLoggerCallBack.Log);
145+
.WithLogging(mSALLogger.Log);
143146
}
144147

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

194197
pApp = cApp.Build();
195198

@@ -300,9 +303,7 @@ internal async static Task<AuthenticationResult> ObtainAccessTokenAsync(
300303
}
301304
else
302305
{
303-
#pragma warning disable CS0618 // Type or member is obsolete
304-
_authenticationResult = await publicAppClient.AcquireTokenByUsernamePassword(scopes, clientCredentials.UserName.UserName, ServiceClient.MakeSecureString(clientCredentials.UserName.Password)).ExecuteAsync().ConfigureAwait(false);
305-
#pragma warning restore CS0618 // Type or member is obsolete
306+
_authenticationResult = await publicAppClient.AcquireTokenByUsernamePassword(scopes, clientCredentials.UserName.UserName, clientCredentials.UserName.Password).ExecuteAsync().ConfigureAwait(false);
306307
}
307308
}
308309
else

src/GeneralTools/DataverseClient/Client/Builder/AbstractClientRequestBuilder.cs

+10
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,16 @@ internal OrganizationRequest BuildRequest(OrganizationRequest request)
133133
parameters.Add(RequestBinderUtil.HEADERLIST, new Dictionary<string,string>(_headers));
134134
}
135135

136+
if (_crmUserId != null && _crmUserId != Guid.Empty)
137+
{
138+
parameters.Add(RequestHeaders.CALLER_OBJECT_ID_HTTP_HEADER, _crmUserId.Value);
139+
}
140+
141+
if (_aadOidId != null && _aadOidId != Guid.Empty)
142+
{
143+
parameters.Add(RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER, _aadOidId.Value);
144+
}
145+
136146
request.Parameters.AddRange(parameters);
137147

138148
// Clear in case this is reused.

src/GeneralTools/DataverseClient/Client/ConnectionService.cs

+7-11
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ internal bool CalledbyExecuteRequest
365365
/// <summary>
366366
/// Logging provider for DataverseConnectionServiceobject.
367367
/// </summary>
368-
private DataverseTraceLogger logEntry { get; set; }
368+
internal DataverseTraceLogger logEntry { get; set; }
369369

370370
/// <summary>
371371
/// Returns Logs from this process.
@@ -2472,18 +2472,14 @@ private bool ShouldRetryWebAPI(Exception ex, int retryCount, int maxRetryCount,
24722472
errorCode == ((int)ErrorCodes.ThrottlingTimeExceededError).ToString() ||
24732473
errorCode == ((int)ErrorCodes.ThrottlingConcurrencyLimitExceededError).ToString())
24742474
{
2475-
if (errorCode == ((int)ErrorCodes.ThrottlingBurstRequestLimitExceededError).ToString())
2476-
{
2477-
// Use Retry-After delay when specified
2478-
if (httpOperationException.Response.Headers.ContainsKey("Retry-After"))
2479-
_retryPauseTimeRunning = TimeSpan.Parse(httpOperationException.Response.Headers["Retry-After"].FirstOrDefault());
2480-
else
2481-
_retryPauseTimeRunning = retryPauseTime.Add(TimeSpan.FromSeconds(Math.Pow(2, retryCount))); ; // default timespan with back off is response does not contain the tag..
2475+
if (httpOperationException.Response.Headers.TryGetValue("Retry-After", out var retryAfter) && double.TryParse(retryAfter.FirstOrDefault(), out var retrySeconds))
2476+
{
2477+
// Note: Retry-After header is in seconds.
2478+
_retryPauseTimeRunning = TimeSpan.FromSeconds(retrySeconds);
24822479
}
24832480
else
2484-
{
2485-
// else use exponential back off delay
2486-
_retryPauseTimeRunning = retryPauseTime.Add(TimeSpan.FromSeconds(Math.Pow(2, retryCount)));
2481+
{
2482+
_retryPauseTimeRunning = retryPauseTime.Add(TimeSpan.FromSeconds(Math.Pow(2, retryCount))); ; // default timespan with back off is response does not contain the tag..
24872483
}
24882484
isThrottlingRetry = true;
24892485
return true;

src/GeneralTools/DataverseClient/Client/DataverseTelemetryBehaviors.cs

+57-18
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public DataverseTelemetryBehaviors(ConnectionService cli)
4646

4747
// reading overrides from app config if present..
4848
// these values override the values that are set on the client from the server.
49-
DataverseTraceLogger logg = new DataverseTraceLogger();
49+
DataverseTraceLogger logg = _callerCdsConnectionServiceHandler.logEntry;
5050
try
5151
{
5252
// Initialize user agent
@@ -107,22 +107,18 @@ public DataverseTelemetryBehaviors(ConnectionService cli)
107107
if (_maxBufferPoolSize < MAXBUFFERPOOLDEFAULT)
108108
{
109109
_maxBufferPoolSize = -1;
110-
logg.Log($"Failed to set MaxBufferPoolSizeOveride property. Value found: {maxBufferPoolSz}. Size must be larger then {MAXBUFFERPOOLDEFAULT}.", System.Diagnostics.TraceEventType.Warning);
110+
logg.Log($"Failed to set MaxBufferPoolSizeOverride property. Value found: {maxBufferPoolSz}. Size must be larger then {MAXBUFFERPOOLDEFAULT}.", System.Diagnostics.TraceEventType.Warning);
111111
}
112112
}
113113
}
114114
else
115-
logg.Log($"Failed to parse MaxBufferPoolSizeOveride property. Value found: {maxBufferPoolSz}. MaxReceivedMessageSizeOverride must be a valid integer.", System.Diagnostics.TraceEventType.Warning);
115+
logg.Log($"Failed to parse MaxBufferPoolSizeOverride property. Value found: {maxBufferPoolSz}. MaxReceivedMessageSizeOverride must be a valid integer.", System.Diagnostics.TraceEventType.Warning);
116116
}
117117

118118
}
119119
catch (Exception ex)
120120
{
121-
logg.Log("Failed to process binding override properties, Only MaxFaultSizeOverride, MaxReceivedMessageSizeOverride and MaxBufferPoolSizeOveride are supported and must be integers.", System.Diagnostics.TraceEventType.Warning, ex);
122-
}
123-
finally
124-
{
125-
logg.Dispose();
121+
logg.Log("Failed to process binding override properties, Only MaxFaultSizeOverride, MaxReceivedMessageSizeOverride and MaxBufferPoolSizeOverride are supported and must be integers.", System.Diagnostics.TraceEventType.Warning, ex);
126122
}
127123
}
128124

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

257+
string[] CrmUserIdList = null;
258+
string[] AADOidList = null;
261259
if (httpRequestMessage != null)
262260
{
263261
httpRequestMessage.Headers[Utilities.RequestHeaders.USER_AGENT_HTTP_HEADER] = _userAgent;
@@ -282,27 +280,68 @@ public object BeforeSendRequest(ref Message request, IClientChannel channel)
282280
Utilities.CleanUpHeaderKeys(httpRequestMessage.Headers);
283281
if (httpRequestMessageObject == null)
284282
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
283+
284+
CrmUserIdList = httpRequestMessage.Headers.GetValues(Utilities.RequestHeaders.CALLER_OBJECT_ID_HTTP_HEADER);
285+
AADOidList = httpRequestMessage.Headers.GetValues(Utilities.RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER);
286+
285287
}
286288

287289
// Adding SOAP headers
288290
Guid callerId = Guid.Empty;
289-
if (_callerCdsConnectionServiceHandler != null)
291+
Guid AADOId = Guid.Empty;
292+
if (CrmUserIdList != null && CrmUserIdList.Length > 0)
293+
{
294+
if(!Guid.TryParse(CrmUserIdList[0], out callerId))
295+
_callerCdsConnectionServiceHandler.logEntry.Log("Failed to parse Caller Object ID from the HTTP Header.", System.Diagnostics.TraceEventType.Warning);
296+
CrmUserIdList = null; // Clear the list.
297+
}
298+
299+
if (AADOidList != null && AADOidList.Length > 0)
290300
{
291-
if (_callerCdsConnectionServiceHandler.WebClient != null)
292-
callerId = _callerCdsConnectionServiceHandler.WebClient.CallerId;
293-
if (_callerCdsConnectionServiceHandler.OnPremClient != null)
294-
callerId = _callerCdsConnectionServiceHandler.OnPremClient.CallerId;
301+
if (!Guid.TryParse(AADOidList[0], out AADOId))
302+
_callerCdsConnectionServiceHandler.logEntry.Log("Failed to parse AADObjectID from the HTTP Header.", System.Diagnostics.TraceEventType.Warning);
303+
AADOidList = null; // Clear the list.
295304
}
296305

297-
if (callerId == Guid.Empty) // Prefer the Caller ID over the AADObjectID.
306+
if (callerId == Guid.Empty && AADOId == Guid.Empty)
307+
{
308+
if (_callerCdsConnectionServiceHandler != null)
309+
{
310+
if (_callerCdsConnectionServiceHandler.WebClient != null)
311+
callerId = _callerCdsConnectionServiceHandler.WebClient.CallerId;
312+
if (_callerCdsConnectionServiceHandler.OnPremClient != null)
313+
callerId = _callerCdsConnectionServiceHandler.OnPremClient.CallerId;
314+
}
315+
316+
if (callerId == Guid.Empty) // Prefer the Caller ID over the AADObjectID.
317+
{
318+
if (_callerCdsConnectionServiceHandler != null && (_callerCdsConnectionServiceHandler.CallerAADObjectId.HasValue && _callerCdsConnectionServiceHandler.CallerAADObjectId.Value != Guid.Empty))
319+
{
320+
// Add Caller ID to the SOAP Envelope.
321+
// Set a header request with the AAD Caller Object ID.
322+
using (OperationContextScope scope = new OperationContextScope((IContextChannel)channel))
323+
{
324+
var AADCallerIdHeader = new MessageHeader<Guid>(_callerCdsConnectionServiceHandler.CallerAADObjectId.Value).GetUntypedHeader(Utilities.RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER, "http://schemas.microsoft.com/xrm/2011/Contracts");
325+
request.Headers.Add(AADCallerIdHeader);
326+
}
327+
}
328+
}
329+
}
330+
else
298331
{
299-
if (_callerCdsConnectionServiceHandler != null && (_callerCdsConnectionServiceHandler.CallerAADObjectId.HasValue && _callerCdsConnectionServiceHandler.CallerAADObjectId.Value != Guid.Empty))
332+
if ( callerId != Guid.Empty )
333+
{
334+
using (OperationContextScope scope = new OperationContextScope((IContextChannel)channel))
335+
{
336+
var CallerIdHeader = new MessageHeader<Guid>(callerId).GetUntypedHeader(Xrm.Sdk.Client.SdkHeaders.CallerId, Xrm.Sdk.XmlNamespaces.V5.Contracts);
337+
request.Headers.Add(CallerIdHeader);
338+
}
339+
}
340+
else if (AADOId != Guid.Empty)
300341
{
301-
// Add Caller ID to the SOAP Envelope.
302-
// Set a header request with the AAD Caller Object ID.
303342
using (OperationContextScope scope = new OperationContextScope((IContextChannel)channel))
304343
{
305-
var AADCallerIdHeader = new MessageHeader<Guid>(_callerCdsConnectionServiceHandler.CallerAADObjectId.Value).GetUntypedHeader(Utilities.RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER, "http://schemas.microsoft.com/xrm/2011/Contracts");
344+
var AADCallerIdHeader = new MessageHeader<Guid>(AADOId).GetUntypedHeader(Utilities.RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER, "http://schemas.microsoft.com/xrm/2011/Contracts");
306345
request.Headers.Add(AADCallerIdHeader);
307346
}
308347
}

src/GeneralTools/DataverseClient/Client/ServiceClient.cs

+4-5
Original file line numberDiff line numberDiff line change
@@ -2039,11 +2039,10 @@ private bool ShouldRetry(OrganizationRequest req, Exception ex, int retryCount,
20392039
OrgEx.Detail.ErrorCode == ErrorCodes.ThrottlingTimeExceededError ||
20402040
OrgEx.Detail.ErrorCode == ErrorCodes.ThrottlingConcurrencyLimitExceededError)
20412041
{
2042-
// Error was raised by a instance throttle trigger.
2043-
if (OrgEx.Detail.ErrorCode == ErrorCodes.ThrottlingBurstRequestLimitExceededError)
2044-
{
2045-
// Use Retry-After delay when specified
2046-
_retryPauseTimeRunning = (TimeSpan)OrgEx.Detail.ErrorDetails["Retry-After"];
2042+
// Use Retry-After delay when specified
2043+
if (OrgEx.Detail.ErrorDetails.TryGetValue("Retry-After", out var retryAfter) && retryAfter is TimeSpan retryAsTimeSpan)
2044+
{
2045+
_retryPauseTimeRunning = retryAsTimeSpan;
20472046
}
20482047
else
20492048
{

src/GeneralTools/DataverseClient/Client/Utils/MSALLoggerCallBack.cs

+28-12
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,42 @@ namespace Microsoft.PowerPlatform.Dataverse.Client.Utils
88
/// <summary>
99
/// This class will be used to support hooking into MSAL Call back logic.
1010
/// </summary>
11-
internal static class MSALLoggerCallBack
11+
internal class MSALLoggerCallBack
1212
{
13-
private static DataverseTraceLogger _logEntry;
13+
14+
public DataverseTraceLogger LogSink { get; set; } = null;
1415

1516
/// <summary>
1617
/// Enabled PII logging for this connection.
1718
/// if this flag is set, it will override the value from app config.
1819
/// </summary>
19-
public static bool? EnabledPIILogging { get; set; } = null;
20+
public bool? EnabledPIILogging { get; set; } = null;
21+
22+
public MSALLoggerCallBack(DataverseTraceLogger logSink = null, bool? enabledPIILogging = null)
23+
{
24+
LogSink = logSink;
25+
EnabledPIILogging = enabledPIILogging;
26+
}
2027

2128
/// <summary>
2229
///
2330
/// </summary>
2431
/// <param name="level"></param>
2532
/// <param name="message"></param>
2633
/// <param name="containsPii"></param>
27-
static public void Log(LogLevel level, string message, bool containsPii)
34+
public void Log(LogLevel level, string message, bool containsPii)
2835
{
29-
if (_logEntry == null)
30-
_logEntry = new DataverseTraceLogger(typeof(LogCallback).Assembly.GetName().Name); // set up logging client.
36+
bool createdLogSource = false;
37+
if (LogSink == null)
38+
{
39+
createdLogSource = true;
40+
LogSink = new DataverseTraceLogger(typeof(LogCallback).Assembly.GetName().Name); // set up logging client.
41+
}
3142

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

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

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

4657
switch (level)
4758
{
4859
case LogLevel.Info:
49-
_logEntry.Log(message, System.Diagnostics.TraceEventType.Information);
60+
LogSink.Log(message, System.Diagnostics.TraceEventType.Information);
5061
break;
5162
case LogLevel.Verbose:
52-
_logEntry.Log(message, System.Diagnostics.TraceEventType.Verbose);
63+
LogSink.Log(message, System.Diagnostics.TraceEventType.Verbose);
5364
break;
5465
case LogLevel.Warning:
55-
_logEntry.Log(message, System.Diagnostics.TraceEventType.Warning);
66+
LogSink.Log(message, System.Diagnostics.TraceEventType.Warning);
5667
break;
5768
case LogLevel.Error:
58-
_logEntry.Log(message, System.Diagnostics.TraceEventType.Error);
69+
LogSink.Log(message, System.Diagnostics.TraceEventType.Error);
5970
break;
6071
default:
6172
break;
6273
}
74+
75+
if (createdLogSource)
76+
{
77+
LogSink.Dispose();
78+
}
6379
}
6480

6581
}

src/GeneralTools/DataverseClient/Client/Utils/RequestBinderUtil.cs

+12
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,23 @@ internal static void ProcessRequestBinderProperties(HttpRequestMessageProperty h
4444
}
4545
continue;
4646
}
47+
if (itm.Key == Utilities.RequestHeaders.CALLER_OBJECT_ID_HTTP_HEADER)
48+
{
49+
AddorUpdateHeaderProperties(httpRequestMessageHeaders, Utilities.RequestHeaders.CALLER_OBJECT_ID_HTTP_HEADER, itm.Value.ToString());
50+
continue;
51+
}
52+
if (itm.Key == Utilities.RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER)
53+
{
54+
AddorUpdateHeaderProperties(httpRequestMessageHeaders, Utilities.RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER, itm.Value.ToString());
55+
continue;
56+
}
4757
}
4858
if ( request.Parameters.Count > 0 )
4959
{
5060
request.Parameters.Remove(Utilities.RequestHeaders.X_MS_CORRELATION_REQUEST_ID);
5161
request.Parameters.Remove(Utilities.RequestHeaders.X_MS_CLIENT_SESSION_ID);
62+
request.Parameters.Remove(Utilities.RequestHeaders.CALLER_OBJECT_ID_HTTP_HEADER);
63+
request.Parameters.Remove(Utilities.RequestHeaders.AAD_CALLER_OBJECT_ID_HTTP_HEADER);
5264
request.Parameters.Remove(HEADERLIST);
5365
}
5466
}

0 commit comments

Comments
 (0)