Skip to content

Commit 1fcce43

Browse files
Adds support for simulating Graph connector TAC notifications. Closes #594 (#596)
1 parent f6fd826 commit 1fcce43

20 files changed

+649
-29
lines changed

dev-proxy-abstractions/IProxyLogger.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
3-
using System.Runtime.Serialization;
43
using Titanium.Web.Proxy.EventArguments;
54
using Microsoft.Extensions.Logging;
65

@@ -33,4 +32,5 @@ public interface IProxyLogger : ICloneable, ILogger
3332
{
3433
public LogLevel LogLevel { get; set; }
3534
public void LogRequest(string[] message, MessageType messageType, LoggingContext? context = null);
35+
public void LogRequest(string[] message, MessageType messageType, string method, string url);
3636
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System.Text.Json;
5+
6+
namespace Microsoft.DevProxy.Abstractions;
7+
8+
// from https://stackoverflow.com/questions/61553962/getting-nested-properties-with-system-text-json
9+
public static partial class JsonExtensions
10+
{
11+
public static JsonElement? Get(this JsonElement element, string name) =>
12+
element.ValueKind != JsonValueKind.Null && element.ValueKind != JsonValueKind.Undefined && element.TryGetProperty(name, out var value)
13+
? value : (JsonElement?)null;
14+
15+
public static JsonElement? Get(this JsonElement element, int index)
16+
{
17+
if (element.ValueKind == JsonValueKind.Null || element.ValueKind == JsonValueKind.Undefined)
18+
return null;
19+
// Throw if index < 0
20+
return index < element.GetArrayLength() ? element[index] : null;
21+
}
22+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System.Text.Json.Serialization;
5+
6+
namespace Microsoft.DevProxy.Abstractions;
7+
8+
public class MockRequest
9+
{
10+
[JsonPropertyName("url")]
11+
public string Url { get; set; } = string.Empty;
12+
[JsonPropertyName("method")]
13+
public string Method { get; set; } = "GET";
14+
[JsonPropertyName("body")]
15+
public dynamic? Body { get; set; }
16+
[JsonPropertyName("headers")]
17+
public List<MockRequestHeader>? Headers { get; set; }
18+
}
19+
20+
public class MockRequestHeader
21+
{
22+
[JsonPropertyName("name")]
23+
public string Name { get; set; } = string.Empty;
24+
[JsonPropertyName("value")]
25+
public string Value { get; set; } = string.Empty;
26+
27+
public MockRequestHeader()
28+
{
29+
}
30+
31+
public MockRequestHeader(string name, string value)
32+
{
33+
Name = name;
34+
Value = value;
35+
}
36+
}

dev-proxy-abstractions/PluginEvents.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.CommandLine;
55
using System.CommandLine.Invocation;
6+
using System.Security.Cryptography.X509Certificates;
67
using Titanium.Web.Proxy.EventArguments;
78
using Titanium.Web.Proxy.Http;
89

@@ -12,6 +13,7 @@ public interface IProxyContext
1213
{
1314
IProxyConfiguration Configuration { get; }
1415
IProxyLogger Logger { get; }
16+
X509Certificate2? Certificate { get; }
1517
}
1618

1719
public class ThrottlerInfo
@@ -182,6 +184,10 @@ public interface IPluginEvents
182184
/// Raised after recording request logs has stopped.
183185
/// </summary>
184186
event AsyncEventHandler<RecordingArgs>? AfterRecordingStop;
187+
/// <summary>
188+
/// Raised when user requested issuing mock requests.
189+
/// </summary>
190+
event AsyncEventHandler<EventArgs>? MockRequest;
185191
}
186192

187193
public class PluginEvents : IPluginEvents
@@ -200,6 +206,7 @@ public class PluginEvents : IPluginEvents
200206
public event EventHandler<RequestLogArgs>? AfterRequestLog;
201207
/// <inheritdoc />
202208
public event AsyncEventHandler<RecordingArgs>? AfterRecordingStop;
209+
public event AsyncEventHandler<EventArgs>? MockRequest;
203210

204211
public void RaiseInit(InitArgs args)
205212
{
@@ -247,4 +254,12 @@ public async Task RaiseRecordingStopped(RecordingArgs args)
247254
await AfterRecordingStop.InvokeAsync(this, args, null);
248255
}
249256
}
257+
258+
public async Task RaiseMockRequest(EventArgs args)
259+
{
260+
if (MockRequest is not null)
261+
{
262+
await MockRequest.InvokeAsync(this, args, null);
263+
}
264+
}
250265
}

dev-proxy-plugins/Behavior/RateLimitingCustomResponseLoader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
using Microsoft.Extensions.Logging;
55
using Microsoft.DevProxy.Abstractions;
6-
using Microsoft.DevProxy.Plugins.MockResponses;
6+
using Microsoft.DevProxy.Plugins.Mocks;
77
using System.Text.Json;
88

99
namespace Microsoft.DevProxy.Plugins.Behavior;

dev-proxy-plugins/MockResponses/CrudApiDefinitionLoader.cs renamed to dev-proxy-plugins/Mocks/CrudApiDefinitionLoader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
using Microsoft.Extensions.Logging;
66
using System.Text.Json;
77

8-
namespace Microsoft.DevProxy.Plugins.MockResponses;
8+
namespace Microsoft.DevProxy.Plugins.Mocks;
99

1010
internal class CrudApiDefinitionLoader : IDisposable
1111
{

dev-proxy-plugins/MockResponses/CrudApiPlugin.cs renamed to dev-proxy-plugins/Mocks/CrudApiPlugin.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
using Microsoft.IdentityModel.Protocols;
2020
using System.Security.Claims;
2121

22-
namespace Microsoft.DevProxy.Plugins.MockResponses;
22+
namespace Microsoft.DevProxy.Plugins.Mocks;
2323

2424
public enum CrudApiActionType
2525
{

dev-proxy-plugins/MockResponses/EntraMockResponsePlugin.cs renamed to dev-proxy-plugins/Mocks/EntraMockResponsePlugin.cs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
using System.Security.Cryptography.X509Certificates;
45
using System.Text;
56
using System.Text.Json;
67
using System.Text.Json.Serialization;
78
using System.Text.RegularExpressions;
89
using System.Web;
910
using Microsoft.DevProxy.Abstractions;
1011

11-
namespace Microsoft.DevProxy.Plugins.MockResponses;
12+
namespace Microsoft.DevProxy.Plugins.Mocks;
1213

13-
class IdToken {
14+
class IdToken
15+
{
1416
[JsonPropertyName("aud")]
1517
public string? Aud { get; set; }
1618
[JsonPropertyName("iss")]
@@ -56,13 +58,38 @@ protected override void ProcessMockResponse(ref byte[] body, IList<MockResponseH
5658
StoreLastNonce(e);
5759
UpdateMsalState(ref bodyString, e, ref changed);
5860
UpdateIdToken(ref bodyString, e, ref changed);
61+
UpdateDevProxyKeyId(ref bodyString, ref changed);
62+
UpdateDevProxyCertificateChain(ref bodyString, ref changed);
5963

6064
if (changed)
6165
{
6266
body = Encoding.UTF8.GetBytes(bodyString);
6367
}
6468
}
6569

70+
private void UpdateDevProxyCertificateChain(ref string bodyString, ref bool changed)
71+
{
72+
if (!bodyString.Contains("@dynamic.devProxyCertificateChain"))
73+
{
74+
return;
75+
}
76+
77+
var certificateChain = GetCertificateChain().First();
78+
bodyString = bodyString.Replace("@dynamic.devProxyCertificateChain", certificateChain);
79+
changed = true;
80+
}
81+
82+
private void UpdateDevProxyKeyId(ref string bodyString, ref bool changed)
83+
{
84+
if (!bodyString.Contains("@dynamic.devProxyKeyId"))
85+
{
86+
return;
87+
}
88+
89+
bodyString = bodyString.Replace("@dynamic.devProxyKeyId", GetKeyId());
90+
changed = true;
91+
}
92+
6693
private void StoreLastNonce(ProxyRequestArgs e)
6794
{
6895
if (e.Session.HttpClient.Request.RequestUri.Query.Contains("nonce="))
@@ -94,7 +121,7 @@ private void UpdateIdToken(ref string body, ProxyRequestArgs e, ref bool changed
94121
{
95122
return;
96123
}
97-
124+
98125
token.Nonce = lastNonce;
99126

100127
tokenChunks[1] = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(token)));
@@ -121,4 +148,31 @@ private void UpdateMsalState(ref string body, ProxyRequestArgs e, ref bool chang
121148
body = body.Replace("state=@dynamic", $"state={msalState}");
122149
changed = true;
123150
}
151+
152+
private string GetKeyId()
153+
{
154+
return _context?.Certificate?.Thumbprint ?? "";
155+
}
156+
157+
private List<string> GetCertificateChain()
158+
{
159+
if (_context?.Certificate is null)
160+
{
161+
return new List<string>();
162+
}
163+
164+
var collection = new X509Certificate2Collection
165+
{
166+
_context.Certificate
167+
};
168+
169+
var certificateChain = new List<string>();
170+
foreach (var certificate in collection)
171+
{
172+
var base64String = Convert.ToBase64String(certificate.RawData);
173+
certificateChain.Add(base64String);
174+
}
175+
176+
return certificateChain;
177+
}
124178
}

0 commit comments

Comments
 (0)