I have an authorization filter to be applied to all controller's actions in order to secure application's endpoints (e.g CustomAuthorizationFilter : IResourceFilter, IFilterMetadata)
This custom filter can be added in startup.cs like this:
services.AddControllers(o =>
{
o.Filters.Add<CustomAuthorizationFilter>();
//other stuff here...
});
For my use case I want to ensure at 100% that all APIs(and its controllers) are secured with this filter, in other words I want to ensure that MyApp.API.Server .csproj project references in its startup.cs class the CustomAuthorizationFilter.
What I start doing is to create a SecurityAttributeReader class in MyApp.API.Server
to parse the project configuration and check if this filter exists or not:
public class SecurityAttributeReader
{
public bool AuthorizationFilterExists()
{
return false;
}
}
My problem I can not fond a solution to retrieve this information.
Can you please challenge me with your ideas :)
Thank you!
So that's was my problem and no I'm updating the post with the solution I found more relevant in my case:
The idea is to create a Roslyn analyzer that’ll be executed at build time and this analyzer will read the startup.cs file to check if the target ‘CustomAuthorizationFilter’ is referenced or not:
public class FilterAnalyser : DiagnosticAnalyzer
{
private const string DiagnosticId = "AnalyserId";
private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(
DiagnosticId,
"title",
"message format",
"category",
DiagnosticSeverity.Error,
isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterCodeBlockStartAction<SyntaxKind>(cb =>
{
bool filterInvocationFound = false;
Location errorLocation = null;
// We only care about method bodies.
if (cb.OwningSymbol.Kind != SymbolKind.Method)return;
// We only care about methods with parameters.
IMethodSymbol method = (IMethodSymbol)cb.OwningSymbol;
if (method.Parameters.IsEmpty)return;
// we only care about Startup.cs class
if (method.ContainingType == null || method.ContainingType.Name != "Startup")return;
cb.RegisterSyntaxNodeAction(ctx =>
{
if (!(ctx.Node is InvocationExpressionSyntax node)) return;
if (!(node.Expression is MemberAccessExpressionSyntax expression)) return;
if (expression.Parent.Ancestors().Any(c => c.ToString().Contains("CustomAuthorizationFilter")))
filterInvocationFound = true;
}, SyntaxKind.InvocationExpression);
cb.RegisterCodeBlockEndAction(ctx =>
{
if (!filterInvocationFound )
{
var diag = Diagnostic.Create(Rule, method.Locations[0]);
ctx.ReportDiagnostic(diag);
}
});
});
}
}