How to retrieve the list of AspNetCore Mvc Filters (type of FilterCollection) used with services.AddControllers in startup.cs programmatically?

Viewed 357

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);
                    }
                });
            });
        }
    }
1 Answers

It is interesting as @Kirk Larkin said. You could create a CheckAttributesFilter to check all actions, but a new question comes that how could we confirm this CheckAttributesFilter works for all actions.

public void OnActionExecuting(ActionExecutingContext context)
    {
        var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
        if (controllerActionDescriptor != null)
        {
            var isDefined = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true)
                .Any(a => a.GetType().Equals(typeof(CustomAuthorizationFilter)));
        }
    }
Related