Skip to content

Commit cd8770e

Browse files
Extend ApiCenterMinimalPermissionsPlugin to support delegated and application permissions (#1418)
* Add SchemeName optional parameter to ApiCenterMinimalPermissionsPlugin * Evaluate minimal permissions based on schemeName value * Extend ApiCenterMinimalPermissionsPluginReport report with schemeName if defined * add logging schemeName if defined --------- Co-authored-by: Waldek Mastykarz <[email protected]>
1 parent fd15fb5 commit cd8770e

File tree

3 files changed

+53
-15
lines changed

3 files changed

+53
-15
lines changed

DevProxy.Plugins/Reporting/ApiCenterMinimalPermissionsPlugin.cs

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public sealed class ApiCenterMinimalPermissionsPluginConfiguration
2222
public string ServiceName { get; set; } = "";
2323
public string SubscriptionId { get; set; } = "";
2424
public string WorkspaceName { get; set; } = "default";
25+
public string? SchemeName { get; set; }
2526
}
2627

2728
public sealed class ApiCenterMinimalPermissionsPlugin(
@@ -143,7 +144,8 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
143144
TokenPermissions = [.. minimalPermissions.TokenPermissions.Distinct()],
144145
MinimalPermissions = minimalPermissions.MinimalScopes,
145146
ExcessivePermissions = [.. minimalPermissions.TokenPermissions.Except(minimalPermissions.MinimalScopes)],
146-
UsesMinimalPermissions = !minimalPermissions.TokenPermissions.Except(minimalPermissions.MinimalScopes).Any()
147+
UsesMinimalPermissions = !minimalPermissions.TokenPermissions.Except(minimalPermissions.MinimalScopes).Any(),
148+
SchemeName = Configuration.SchemeName
147149
};
148150
results.Add(result);
149151

@@ -155,20 +157,45 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
155157

156158
if (result.UsesMinimalPermissions)
157159
{
158-
Logger.LogInformation(
159-
"API {ApiName} is called with minimal permissions: {MinimalPermissions}",
160-
result.ApiName,
161-
string.Join(", ", result.MinimalPermissions)
162-
);
160+
if (string.IsNullOrWhiteSpace(Configuration.SchemeName))
161+
{
162+
Logger.LogInformation(
163+
"API {ApiName} is called with minimal permissions: {MinimalPermissions}",
164+
result.ApiName,
165+
string.Join(", ", result.MinimalPermissions)
166+
);
167+
}
168+
else
169+
{
170+
Logger.LogInformation(
171+
"API {ApiName} is called with minimal permissions of '{SchemeName}' scheme: {MinimalPermissions}",
172+
result.ApiName,
173+
Configuration.SchemeName,
174+
string.Join(", ", result.MinimalPermissions)
175+
);
176+
}
163177
}
164178
else
165179
{
166-
Logger.LogWarning(
167-
"Calling API {ApiName} with excessive permissions: {ExcessivePermissions}. Minimal permissions are: {MinimalPermissions}",
168-
result.ApiName,
169-
string.Join(", ", result.ExcessivePermissions),
170-
string.Join(", ", result.MinimalPermissions)
171-
);
180+
if (string.IsNullOrWhiteSpace(Configuration.SchemeName))
181+
{
182+
Logger.LogWarning(
183+
"Calling API {ApiName} with excessive permissions: {ExcessivePermissions}. Minimal permissions are: {MinimalPermissions}",
184+
result.ApiName,
185+
string.Join(", ", result.ExcessivePermissions),
186+
string.Join(", ", result.MinimalPermissions)
187+
);
188+
}
189+
else
190+
{
191+
Logger.LogWarning(
192+
"Calling API {ApiName} with excessive permissions of '{SchemeName}' scheme: {ExcessivePermissions}. Minimal permissions are: {MinimalPermissions}",
193+
result.ApiName,
194+
Configuration.SchemeName,
195+
string.Join(", ", result.ExcessivePermissions),
196+
string.Join(", ", result.MinimalPermissions)
197+
);
198+
}
172199
}
173200

174201
if (unmatchedApiRequests.Any())
@@ -208,7 +235,7 @@ private ApiPermissionsInfo CheckMinimalPermissions(IEnumerable<RequestLog> reque
208235
{
209236
Logger.LogInformation("Checking minimal permissions for API {ApiName}...", apiDefinition.Definition!.Servers.First().Url);
210237

211-
return apiDefinition.Definition.CheckMinimalPermissions(requests, Logger);
238+
return apiDefinition.Definition.CheckMinimalPermissions(requests, Logger, Configuration.SchemeName);
212239
}
213240

214241
private (Dictionary<ApiDefinition, List<RequestLog>> RequestsByApiDefinition, IEnumerable<RequestLog> UnmatchedRequests) GetRequestsByApiDefinition(IEnumerable<RequestLog> interceptedRequests, Dictionary<string, ApiDefinition> apiDefinitionsByUrl)

DevProxy.Plugins/Reporting/ApiCenterMinimalPermissionsPluginReport.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public sealed class ApiCenterMinimalPermissionsPluginReportApiResult
1919
public required IEnumerable<string> Requests { get; init; }
2020
public required IEnumerable<string> TokenPermissions { get; init; }
2121
public required bool UsesMinimalPermissions { get; init; }
22+
public string? SchemeName { get; init; }
2223
}
2324

2425
public sealed class ApiCenterMinimalPermissionsPluginReport : IMarkdownReport, IPlainTextReport
@@ -52,11 +53,14 @@ public string ToMarkdown()
5253
{
5354
foreach (var apiResult in Results)
5455
{
56+
var permissionsHeader = "#### Permissions" + (string.IsNullOrWhiteSpace(apiResult.SchemeName)
57+
? "" : $" for {apiResult.SchemeName} scheme");
58+
5559
_ = sb.AppendFormat(CultureInfo.InvariantCulture, "### {0}{1}", apiResult.ApiName, Environment.NewLine)
5660
.AppendLine()
5761
.AppendFormat(CultureInfo.InvariantCulture, apiResult.UsesMinimalPermissions ? "✅ Called using minimal permissions{0}" : "🛑 Called using excessive permissions{0}", Environment.NewLine)
5862
.AppendLine()
59-
.AppendLine("#### Permissions")
63+
.AppendLine(permissionsHeader)
6064
.AppendLine()
6165
.AppendFormat(CultureInfo.InvariantCulture, "- Minimal permissions: {0}{1}", string.Join(", ", apiResult.MinimalPermissions.Order().Select(p => $"`{p}`")), Environment.NewLine)
6266
.AppendFormat(CultureInfo.InvariantCulture, "- Permissions on the token: {0}{1}", string.Join(", ", apiResult.TokenPermissions.Order().Select(p => $"`{p}`")), Environment.NewLine)
@@ -115,11 +119,14 @@ public string ToMarkdown()
115119
{
116120
foreach (var apiResult in Results)
117121
{
122+
var permissionsHeader = "Permissions" + (string.IsNullOrWhiteSpace(apiResult.SchemeName)
123+
? "" : $" for {apiResult.SchemeName} scheme");
124+
118125
_ = sb.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}", apiResult.ApiName, Environment.NewLine)
119126
.AppendLine()
120127
.AppendLine(apiResult.UsesMinimalPermissions ? "v Called using minimal permissions" : "x Called using excessive permissions")
121128
.AppendLine()
122-
.AppendLine("Permissions")
129+
.AppendLine(permissionsHeader)
123130
.AppendLine()
124131
.AppendFormat(CultureInfo.InvariantCulture, "- Minimal permissions: {0}{1}", string.Join(", ", apiResult.MinimalPermissions.Order()), Environment.NewLine)
125132
.AppendFormat(CultureInfo.InvariantCulture, "- Permissions on the token: {0}{1}", string.Join(", ", apiResult.TokenPermissions.Order()), Environment.NewLine)

schemas/v1.3.0/apicenterminimalpermissionsplugin.schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
"type": "string",
2424
"description": "Name of the Azure API Center workspace to use. Default is 'default'.",
2525
"default": "default"
26+
},
27+
"schemeName": {
28+
"type": "string",
29+
"description": "The name of the security scheme definition. Used to determine minimal permissions required for API calls."
2630
}
2731
},
2832
"required": [

0 commit comments

Comments
 (0)