Skip to content

Commit 826d644

Browse files
committed
Add MethodAndUrl record instead of tuple and MethodAndUrlComparer
1 parent 1cda8dd commit 826d644

File tree

5 files changed

+42
-59
lines changed

5 files changed

+42
-59
lines changed

DevProxy.Plugins/Reporting/GraphMinimalPermissionsGuidancePlugin.cs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,8 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
7777
return;
7878
}
7979

80-
var methodAndUrlComparer = new MethodAndUrlComparer();
81-
var delegatedEndpoints = new List<(string method, string url)>();
82-
var applicationEndpoints = new List<(string method, string url)>();
80+
var delegatedEndpoints = new List<MethodAndUrl>();
81+
var applicationEndpoints = new List<MethodAndUrl>();
8382

8483
// scope for delegated permissions
8584
IEnumerable<string> scopesToEvaluate = [];
@@ -95,20 +94,20 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
9594

9695
var methodAndUrlString = request.Message;
9796
var methodAndUrl = MethodAndUrlUtils.ToMethodAndUrl(methodAndUrlString);
98-
if (methodAndUrl.method.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase))
97+
if (methodAndUrl.Method.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase))
9998
{
10099
continue;
101100
}
102101

103-
if (!ProxyUtils.MatchesUrlToWatch(UrlsToWatch, methodAndUrl.url))
102+
if (!ProxyUtils.MatchesUrlToWatch(UrlsToWatch, methodAndUrl.Url))
104103
{
105-
Logger.LogDebug("URL not matched: {Url}", methodAndUrl.url);
104+
Logger.LogDebug("URL not matched: {Url}", methodAndUrl.Url);
106105
continue;
107106
}
108107

109-
var requestsFromBatch = Array.Empty<(string method, string url)>();
108+
var requestsFromBatch = Array.Empty<MethodAndUrl>();
110109

111-
var uri = new Uri(methodAndUrl.url);
110+
var uri = new Uri(methodAndUrl.Url);
112111
if (!ProxyUtils.IsGraphUrl(uri))
113112
{
114113
continue;
@@ -121,7 +120,7 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
121120
}
122121
else
123122
{
124-
methodAndUrl = (methodAndUrl.method, GetTokenizedUrl(methodAndUrl.url));
123+
methodAndUrl = new(methodAndUrl.Method, GetTokenizedUrl(methodAndUrl.Url));
125124
}
126125

127126
var (type, permissions) = GetPermissionsAndType(request);
@@ -162,8 +161,8 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
162161
}
163162

164163
// Remove duplicates
165-
delegatedEndpoints = [.. delegatedEndpoints.Distinct(methodAndUrlComparer)];
166-
applicationEndpoints = [.. applicationEndpoints.Distinct(methodAndUrlComparer)];
164+
delegatedEndpoints = [.. delegatedEndpoints.Distinct()];
165+
applicationEndpoints = [.. applicationEndpoints.Distinct()];
167166

168167
if (delegatedEndpoints.Count == 0 && applicationEndpoints.Count == 0)
169168
{
@@ -187,7 +186,7 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
187186
var delegatedPermissionsInfo = new GraphMinimalPermissionsInfo();
188187
report.DelegatedPermissions = delegatedPermissionsInfo;
189188

190-
Logger.LogInformation("Evaluating delegated permissions for: {Endpoints}", string.Join(", ", delegatedEndpoints.Select(e => $"{e.method} {e.url}")));
189+
Logger.LogInformation("Evaluating delegated permissions for: {Endpoints}", string.Join(", ", delegatedEndpoints.Select(e => $"{e.Method} {e.Url}")));
191190

192191
await EvaluateMinimalScopesAsync(delegatedEndpoints, scopesToEvaluate, GraphPermissionsType.Delegated, delegatedPermissionsInfo, cancellationToken);
193192
}
@@ -197,7 +196,7 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
197196
var applicationPermissionsInfo = new GraphMinimalPermissionsInfo();
198197
report.ApplicationPermissions = applicationPermissionsInfo;
199198

200-
Logger.LogInformation("Evaluating application permissions for: {Endpoints}", string.Join(", ", applicationEndpoints.Select(e => $"{e.method} {e.url}")));
199+
Logger.LogInformation("Evaluating application permissions for: {Endpoints}", string.Join(", ", applicationEndpoints.Select(e => $"{e.Method} {e.Url}")));
201200

202201
await EvaluateMinimalScopesAsync(applicationEndpoints, rolesToEvaluate, GraphPermissionsType.Application, applicationPermissionsInfo, cancellationToken);
203202
}
@@ -217,7 +216,7 @@ private void InitializePermissionsToExclude()
217216
}
218217

219218
private async Task EvaluateMinimalScopesAsync(
220-
IEnumerable<(string method, string url)> endpoints,
219+
IEnumerable<MethodAndUrl> endpoints,
221220
IEnumerable<string> permissionsFromAccessToken,
222221
GraphPermissionsType scopeType,
223222
GraphMinimalPermissionsInfo permissionsInfo,
@@ -228,12 +227,12 @@ private async Task EvaluateMinimalScopesAsync(
228227
throw new InvalidOperationException("GraphUtils is not initialized. Make sure to call InitializeAsync first.");
229228
}
230229

231-
var payload = endpoints.Select(e => new GraphRequestInfo { Method = e.method, Url = e.url });
230+
var payload = endpoints.Select(e => new GraphRequestInfo { Method = e.Method, Url = e.Url });
232231

233232
permissionsInfo.Operations = [.. endpoints.Select(e => new GraphMinimalPermissionsOperationInfo
234233
{
235-
Method = e.method,
236-
Endpoint = e.url
234+
Method = e.Method,
235+
Endpoint = e.Url
237236
})];
238237
permissionsInfo.PermissionsFromTheToken = permissionsFromAccessToken;
239238

@@ -289,9 +288,9 @@ private async Task EvaluateMinimalScopesAsync(
289288
}
290289
}
291290

292-
private static (string method, string url)[] GetRequestsFromBatch(string batchBody, string graphVersion, string graphHostName)
291+
private static MethodAndUrl[] GetRequestsFromBatch(string batchBody, string graphVersion, string graphHostName)
293292
{
294-
var requests = new List<(string method, string url)>();
293+
var requests = new List<MethodAndUrl>();
295294

296295
if (string.IsNullOrEmpty(batchBody))
297296
{
@@ -313,7 +312,8 @@ private static (string method, string url)[] GetRequestsFromBatch(string batchBo
313312
var method = request.Method;
314313
var url = request.Url;
315314
var absoluteUrl = $"https://{graphHostName}/{graphVersion}{url}";
316-
requests.Add((method, GetTokenizedUrl(absoluteUrl)));
315+
MethodAndUrl methodAndUrl = new(Method: method, Url: GetTokenizedUrl(absoluteUrl));
316+
requests.Add(methodAndUrl);
317317
}
318318
catch { }
319319
}

DevProxy.Plugins/Reporting/GraphMinimalPermissionsPlugin.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
6060
return;
6161
}
6262

63-
var methodAndUrlComparer = new MethodAndUrlComparer();
64-
var endpoints = new List<(string method, string url)>();
63+
var endpoints = new List<MethodAndUrl>();
6564

6665
foreach (var request in e.RequestLogs)
6766
{
@@ -72,18 +71,18 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
7271

7372
var methodAndUrlString = request.Message;
7473
var methodAndUrl = MethodAndUrlUtils.ToMethodAndUrl(methodAndUrlString);
75-
if (methodAndUrl.method.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase))
74+
if (methodAndUrl.Method.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase))
7675
{
7776
continue;
7877
}
7978

80-
if (!ProxyUtils.MatchesUrlToWatch(UrlsToWatch, methodAndUrl.url))
79+
if (!ProxyUtils.MatchesUrlToWatch(UrlsToWatch, methodAndUrl.Url))
8180
{
82-
Logger.LogDebug("URL not matched: {Url}", methodAndUrl.url);
81+
Logger.LogDebug("URL not matched: {Url}", methodAndUrl.Url);
8382
continue;
8483
}
8584

86-
var uri = new Uri(methodAndUrl.url);
85+
var uri = new Uri(methodAndUrl.Url);
8786
if (!ProxyUtils.IsGraphUrl(uri))
8887
{
8988
continue;
@@ -97,21 +96,21 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
9796
}
9897
else
9998
{
100-
methodAndUrl = (methodAndUrl.method, GetTokenizedUrl(methodAndUrl.url));
99+
methodAndUrl = new(methodAndUrl.Method, GetTokenizedUrl(methodAndUrl.Url));
101100
endpoints.Add(methodAndUrl);
102101
}
103102
}
104103

105104
// Remove duplicates
106-
endpoints = [.. endpoints.Distinct(methodAndUrlComparer)];
105+
endpoints = [.. endpoints.Distinct()];
107106

108107
if (endpoints.Count == 0)
109108
{
110109
Logger.LogInformation("No requests to Microsoft Graph endpoints recorded. Will not retrieve minimal permissions.");
111110
return;
112111
}
113112

114-
Logger.LogInformation("Retrieving minimal permissions for:\r\n{Endpoints}\r\n", string.Join(Environment.NewLine, endpoints.Select(e => $"- {e.method} {e.url}")));
113+
Logger.LogInformation("Retrieving minimal permissions for:\r\n{Endpoints}\r\n", string.Join(Environment.NewLine, endpoints.Select(e => $"- {e.Method} {e.Url}")));
115114

116115
Logger.LogWarning("This plugin is in preview and may not return the correct results.\r\nPlease review the permissions and test your app before using them in production.\r\nIf you have any feedback, please open an issue at https://aka.ms/devproxy/issue.\r\n");
117116

@@ -125,15 +124,15 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
125124
}
126125

127126
private async Task<GraphMinimalPermissionsPluginReport?> DetermineMinimalScopesAsync(
128-
IEnumerable<(string method, string url)> endpoints,
127+
IEnumerable<MethodAndUrl> endpoints,
129128
CancellationToken cancellationToken)
130129
{
131130
if (_graphUtils is null)
132131
{
133132
throw new InvalidOperationException("GraphUtils is not initialized. Make sure to call InitializeAsync first.");
134133
}
135134

136-
var payload = endpoints.Select(e => new GraphRequestInfo { Method = e.method, Url = e.url });
135+
var payload = endpoints.Select(e => new GraphRequestInfo { Method = e.Method, Url = e.Url });
137136

138137
try
139138
{
@@ -179,9 +178,9 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
179178
}
180179
}
181180

182-
private static (string method, string url)[] GetRequestsFromBatch(string batchBody, string graphVersion, string graphHostName)
181+
private static MethodAndUrl[] GetRequestsFromBatch(string batchBody, string graphVersion, string graphHostName)
183182
{
184-
var requests = new List<(string, string)>();
183+
var requests = new List<MethodAndUrl>();
185184

186185
if (string.IsNullOrEmpty(batchBody))
187186
{
@@ -203,7 +202,8 @@ private static (string method, string url)[] GetRequestsFromBatch(string batchBo
203202
var method = request.Method;
204203
var url = request.Url;
205204
var absoluteUrl = $"https://{graphHostName}/{graphVersion}{url}";
206-
requests.Add((method, GetTokenizedUrl(absoluteUrl)));
205+
MethodAndUrl methodAndUrl = new(Method: method, Url: GetTokenizedUrl(absoluteUrl));
206+
requests.Add(methodAndUrl);
207207
}
208208
catch { }
209209
}

DevProxy.Plugins/Utils/GraphUtils.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ internal static string GetScopeTypeString(GraphPermissionsType type)
4747
};
4848
}
4949

50-
internal async Task<IEnumerable<string>> UpdateUserScopesAsync(IEnumerable<string> minimalScopes, IEnumerable<(string method, string url)> endpoints, GraphPermissionsType permissionsType)
50+
internal async Task<IEnumerable<string>> UpdateUserScopesAsync(IEnumerable<string> minimalScopes, IEnumerable<MethodAndUrl> endpoints, GraphPermissionsType permissionsType)
5151
{
52-
var userEndpoints = endpoints.Where(e => e.url.Contains("/users/{", StringComparison.OrdinalIgnoreCase));
52+
var userEndpoints = endpoints.Where(e => e.Url.Contains("/users/{", StringComparison.OrdinalIgnoreCase));
5353
if (!userEndpoints.Any())
5454
{
5555
return minimalScopes;
@@ -60,8 +60,8 @@ internal async Task<IEnumerable<string>> UpdateUserScopesAsync(IEnumerable<strin
6060
var url = $"https://devxapi-func-prod-eastus.azurewebsites.net/permissions?scopeType={GetScopeTypeString(permissionsType)}";
6161
var urls = userEndpoints.Select(e =>
6262
{
63-
_logger.LogDebug("Getting permissions for {Method} {Url}", e.method, e.url);
64-
return $"{url}&requesturl={e.url}&method={e.method}";
63+
_logger.LogDebug("Getting permissions for {Method} {Url}", e.Method, e.Url);
64+
return $"{url}&requesturl={e.Url}&method={e.Method}";
6565
});
6666
var tasks = urls.Select(u =>
6767
{

DevProxy.Plugins/Utils/MethodAndUrlComparer.cs

Lines changed: 0 additions & 19 deletions
This file was deleted.

DevProxy.Plugins/Utils/MethodAndUrlUtils.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
namespace DevProxy.Plugins.Utils;
66

7+
internal readonly record struct MethodAndUrl(string Method, string Url);
8+
79
internal static class MethodAndUrlUtils
810
{
9-
public static (string method, string url) ToMethodAndUrl(string methodAndUrlString)
11+
public static MethodAndUrl ToMethodAndUrl(string methodAndUrlString)
1012
{
1113
ArgumentException.ThrowIfNullOrWhiteSpace(methodAndUrlString, nameof(methodAndUrlString));
1214

@@ -15,6 +17,6 @@ public static (string method, string url) ToMethodAndUrl(string methodAndUrlStri
1517
{
1618
info = [info[0], string.Join(" ", info.Skip(1))];
1719
}
18-
return (method: info[0], url: info[1]);
20+
return new(Method: info[0], Url: info[1]);
1921
}
2022
}

0 commit comments

Comments
 (0)