using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Nuuru.Tools.AuthFlowChart.Models; namespace Nuuru.Tools.AuthFlowChart.Analysis; /// /// Main analyzer that processes controller files and extracts auth information /// public class ControllerAnalyzer { private static readonly HashSet HttpMethods = new(StringComparer.OrdinalIgnoreCase) { "HttpGet", "HttpPost", "HttpPut", "HttpDelete", "HttpPatch", "HttpGetAttribute", "HttpPostAttribute", "HttpPutAttribute", "HttpDeleteAttribute", "HttpPatchAttribute" }; public List Analyze(string controllersDirectory) { var controllers = new List(); if (!Directory.Exists(controllersDirectory)) { Console.Error.WriteLine($"Directory not found: {controllersDirectory}"); return controllers; } var files = Directory.GetFiles(controllersDirectory, "*Controller.cs"); foreach (var file in files) { var controller = AnalyzeFile(file); if (controller != null) { controllers.Add(controller); } } return controllers.OrderBy(c => c.Name).ToList(); } private ControllerInfo? AnalyzeFile(string filePath) { var code = File.ReadAllText(filePath); var tree = CSharpSyntaxTree.ParseText(code); var root = tree.GetCompilationUnitRoot(); // Find the controller class var classDecl = root.DescendantNodes() .OfType() .FirstOrDefault(c => IsController(c)); if (classDecl == null) return null; var controller = new ControllerInfo { Name = classDecl.Identifier.Text }; // Extract class-level attributes var (classAuthLevel, classPermission, classRoute) = AttributeVisitor.ExtractFromClass(classDecl); controller.DefaultAuthLevel = classAuthLevel; controller.DefaultPermission = classPermission; // Resolve [controller] token in route var resolvedRoute = classRoute ?? $"api/{controller.DisplayName.ToLowerInvariant()}"; resolvedRoute = resolvedRoute.Replace("[controller]", controller.DisplayName.ToLowerInvariant()); controller.RoutePrefix = resolvedRoute; // Find all action methods (public methods with [Http*] attributes) var methods = classDecl.Members .OfType() .Where(m => IsActionMethod(m)); foreach (var method in methods) { var endpoint = AnalyzeMethod(method, controller); controller.Endpoints.Add(endpoint); } return controller; } private static bool IsController(ClassDeclarationSyntax classDecl) { // Check if it inherits from ControllerBase or has [ApiController] var hasApiControllerAttribute = classDecl.AttributeLists .SelectMany(al => al.Attributes) .Any(a => a.Name.ToString().Contains("ApiController")); var inheritsFromControllerBase = classDecl.BaseList?.Types .Any(t => t.ToString().Contains("Controller")) ?? false; return hasApiControllerAttribute || inheritsFromControllerBase; } private static bool IsActionMethod(MethodDeclarationSyntax method) { // Check if the method is public var isPublic = method.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword)); // Check if the method has any [Http*] attribute var hasHttpAttribute = method.AttributeLists .SelectMany(al => al.Attributes) .Any(a => HttpMethods.Any(h => a.Name.ToString().Equals(h, StringComparison.OrdinalIgnoreCase) || a.Name.ToString().Equals(h.Replace("Attribute", ""), StringComparison.OrdinalIgnoreCase))); return isPublic && hasHttpAttribute; } private EndpointInfo AnalyzeMethod(MethodDeclarationSyntax method, ControllerInfo controller) { var (methodAuthLevel, methodPermission, httpMethod, methodRoute) = AttributeVisitor.ExtractFromMethod(method); var inlineChecks = InlineCheckVisitor.ExtractFromMethod(method); var endpoint = new EndpointInfo { MethodName = method.Identifier.Text, HttpMethod = httpMethod ?? "GET", RouteTemplate = methodRoute ?? "", InlineChecks = inlineChecks.ToList() }; // Determine effective auth level // Method-level attributes override controller-level var hasMethodAuthAttribute = method.AttributeLists .SelectMany(al => al.Attributes) .Any(a => { var name = a.Name.ToString(); return name.Contains("Authorize") || name.Contains("AllowAnonymous"); }); if (hasMethodAuthAttribute) { endpoint.AuthLevel = methodAuthLevel; endpoint.AttributePermission = methodPermission; endpoint.InheritsFromController = false; } else { // Inherit from controller endpoint.AuthLevel = controller.DefaultAuthLevel; endpoint.AttributePermission = controller.DefaultPermission; endpoint.InheritsFromController = true; } return endpoint; } }