Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 40 additions & 13 deletions DevProxy.Plugins/Reporting/ApiCenterMinimalPermissionsPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public sealed class ApiCenterMinimalPermissionsPluginConfiguration
public string ServiceName { get; set; } = "";
public string SubscriptionId { get; set; } = "";
public string WorkspaceName { get; set; } = "default";
public string? SchemeName { get; set; }
}

public sealed class ApiCenterMinimalPermissionsPlugin(
Expand Down Expand Up @@ -143,7 +144,8 @@ public override async Task AfterRecordingStopAsync(RecordingArgs e, Cancellation
TokenPermissions = [.. minimalPermissions.TokenPermissions.Distinct()],
MinimalPermissions = minimalPermissions.MinimalScopes,
ExcessivePermissions = [.. minimalPermissions.TokenPermissions.Except(minimalPermissions.MinimalScopes)],
UsesMinimalPermissions = !minimalPermissions.TokenPermissions.Except(minimalPermissions.MinimalScopes).Any()
UsesMinimalPermissions = !minimalPermissions.TokenPermissions.Except(minimalPermissions.MinimalScopes).Any(),
SchemeName = Configuration.SchemeName
};
results.Add(result);

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

if (result.UsesMinimalPermissions)
{
Logger.LogInformation(
"API {ApiName} is called with minimal permissions: {MinimalPermissions}",
result.ApiName,
string.Join(", ", result.MinimalPermissions)
);
if (string.IsNullOrWhiteSpace(Configuration.SchemeName))
{
Logger.LogInformation(
"API {ApiName} is called with minimal permissions: {MinimalPermissions}",
result.ApiName,
string.Join(", ", result.MinimalPermissions)
);
}
else
{
Logger.LogInformation(
"API {ApiName} is called with minimal permissions of '{SchemeName}' scheme: {MinimalPermissions}",
result.ApiName,
Configuration.SchemeName,
string.Join(", ", result.MinimalPermissions)
);
}
}
else
{
Logger.LogWarning(
"Calling API {ApiName} with excessive permissions: {ExcessivePermissions}. Minimal permissions are: {MinimalPermissions}",
result.ApiName,
string.Join(", ", result.ExcessivePermissions),
string.Join(", ", result.MinimalPermissions)
);
if (string.IsNullOrWhiteSpace(Configuration.SchemeName))
{
Logger.LogWarning(
"Calling API {ApiName} with excessive permissions: {ExcessivePermissions}. Minimal permissions are: {MinimalPermissions}",
result.ApiName,
string.Join(", ", result.ExcessivePermissions),
string.Join(", ", result.MinimalPermissions)
);
}
else
{
Logger.LogWarning(
"Calling API {ApiName} with excessive permissions of '{SchemeName}' scheme: {ExcessivePermissions}. Minimal permissions are: {MinimalPermissions}",
result.ApiName,
Configuration.SchemeName,
string.Join(", ", result.ExcessivePermissions),
string.Join(", ", result.MinimalPermissions)
);
}
}

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

return apiDefinition.Definition.CheckMinimalPermissions(requests, Logger);
return apiDefinition.Definition.CheckMinimalPermissions(requests, Logger, Configuration.SchemeName);
}

private (Dictionary<ApiDefinition, List<RequestLog>> RequestsByApiDefinition, IEnumerable<RequestLog> UnmatchedRequests) GetRequestsByApiDefinition(IEnumerable<RequestLog> interceptedRequests, Dictionary<string, ApiDefinition> apiDefinitionsByUrl)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public sealed class ApiCenterMinimalPermissionsPluginReportApiResult
public required IEnumerable<string> Requests { get; init; }
public required IEnumerable<string> TokenPermissions { get; init; }
public required bool UsesMinimalPermissions { get; init; }
public string? SchemeName { get; init; }
}

public sealed class ApiCenterMinimalPermissionsPluginReport : IMarkdownReport, IPlainTextReport
Expand Down Expand Up @@ -52,11 +53,14 @@ public string ToMarkdown()
{
foreach (var apiResult in Results)
{
var permissionsHeader = "#### Permissions" + (string.IsNullOrWhiteSpace(apiResult.SchemeName)
? "" : $" for {apiResult.SchemeName} scheme");

_ = sb.AppendFormat(CultureInfo.InvariantCulture, "### {0}{1}", apiResult.ApiName, Environment.NewLine)
.AppendLine()
.AppendFormat(CultureInfo.InvariantCulture, apiResult.UsesMinimalPermissions ? "✅ Called using minimal permissions{0}" : "🛑 Called using excessive permissions{0}", Environment.NewLine)
.AppendLine()
.AppendLine("#### Permissions")
.AppendLine(permissionsHeader)
.AppendLine()
.AppendFormat(CultureInfo.InvariantCulture, "- Minimal permissions: {0}{1}", string.Join(", ", apiResult.MinimalPermissions.Order().Select(p => $"`{p}`")), Environment.NewLine)
.AppendFormat(CultureInfo.InvariantCulture, "- Permissions on the token: {0}{1}", string.Join(", ", apiResult.TokenPermissions.Order().Select(p => $"`{p}`")), Environment.NewLine)
Expand Down Expand Up @@ -115,11 +119,14 @@ public string ToMarkdown()
{
foreach (var apiResult in Results)
{
var permissionsHeader = "Permissions" + (string.IsNullOrWhiteSpace(apiResult.SchemeName)
? "" : $" for {apiResult.SchemeName} scheme");

_ = sb.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}", apiResult.ApiName, Environment.NewLine)
.AppendLine()
.AppendLine(apiResult.UsesMinimalPermissions ? "v Called using minimal permissions" : "x Called using excessive permissions")
.AppendLine()
.AppendLine("Permissions")
.AppendLine(permissionsHeader)
.AppendLine()
.AppendFormat(CultureInfo.InvariantCulture, "- Minimal permissions: {0}{1}", string.Join(", ", apiResult.MinimalPermissions.Order()), Environment.NewLine)
.AppendFormat(CultureInfo.InvariantCulture, "- Permissions on the token: {0}{1}", string.Join(", ", apiResult.TokenPermissions.Order()), Environment.NewLine)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
"type": "string",
"description": "Name of the Azure API Center workspace to use. Default is 'default'.",
"default": "default"
},
"schemeName": {
"type": "string",
"description": "The name of the security scheme definition. Used to determine minimal permissions required for API calls."
}
},
"required": [
Expand Down