Skip to content

Commit 5a32df7

Browse files
Add WrongAuthorizationAttributeAnalyzer
1 parent 00e1c5f commit 5a32df7

File tree

19 files changed

+2074
-2
lines changed

19 files changed

+2074
-2
lines changed

src/HotChocolate/Core/src/Types.Analyzers/Errors.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,4 +247,13 @@ public static class Errors
247247
category: "TypeSystem",
248248
DiagnosticSeverity.Error,
249249
isEnabledByDefault: true);
250+
251+
public static readonly DiagnosticDescriptor WrongAuthorizeAttribute =
252+
new(
253+
id: "HC0106",
254+
title: "Microsoft Authorization Attribute Not Allowed",
255+
messageFormat: "Use HotChocolate.Authorization.{0} instead",
256+
category: "TypeSystem",
257+
DiagnosticSeverity.Error,
258+
isEnabledByDefault: true);
250259
}
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
using System.Collections.Immutable;
2+
using Microsoft.CodeAnalysis;
3+
using Microsoft.CodeAnalysis.CSharp;
4+
using Microsoft.CodeAnalysis.CSharp.Syntax;
5+
using Microsoft.CodeAnalysis.Diagnostics;
6+
using static HotChocolate.Types.Analyzers.WellKnownAttributes;
7+
8+
namespace HotChocolate.Types.Analyzers;
9+
10+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
11+
public sealed class WrongAuthorizationAttributeAnalyzer : DiagnosticAnalyzer
12+
{
13+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
14+
[Errors.WrongAuthorizeAttribute];
15+
16+
public override void Initialize(AnalysisContext context)
17+
{
18+
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
19+
context.EnableConcurrentExecution();
20+
context.RegisterSyntaxNodeAction(AnalyzeMethodDeclaration, SyntaxKind.MethodDeclaration);
21+
context.RegisterSyntaxNodeAction(AnalyzePropertyDeclaration, SyntaxKind.PropertyDeclaration);
22+
context.RegisterSyntaxNodeAction(AnalyzeClassDeclaration, SyntaxKind.ClassDeclaration);
23+
context.RegisterSyntaxNodeAction(AnalyzeRecordDeclaration, SyntaxKind.RecordDeclaration);
24+
}
25+
26+
private static void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context)
27+
{
28+
var methodDeclaration = (MethodDeclarationSyntax)context.Node;
29+
30+
// Get the containing type (class or record)
31+
var containingType = methodDeclaration.Parent as TypeDeclarationSyntax;
32+
if (containingType is null)
33+
{
34+
return;
35+
}
36+
37+
// Check if the type has ObjectType or root type attribute
38+
if (!HasGraphQLTypeAttribute(context, containingType))
39+
{
40+
return;
41+
}
42+
43+
AnalyzeAttributesForMember(methodDeclaration.AttributeLists, context);
44+
}
45+
46+
private static void AnalyzePropertyDeclaration(SyntaxNodeAnalysisContext context)
47+
{
48+
var propertyDeclaration = (PropertyDeclarationSyntax)context.Node;
49+
50+
// Get the containing type (class or record)
51+
var containingType = propertyDeclaration.Parent as TypeDeclarationSyntax;
52+
if (containingType is null)
53+
{
54+
return;
55+
}
56+
57+
// Check if the type has ObjectType or root type attribute
58+
if (!HasGraphQLTypeAttribute(context, containingType))
59+
{
60+
return;
61+
}
62+
63+
AnalyzeAttributesForMember(propertyDeclaration.AttributeLists, context);
64+
}
65+
66+
private static void AnalyzeClassDeclaration(SyntaxNodeAnalysisContext context)
67+
{
68+
var classDeclaration = (ClassDeclarationSyntax)context.Node;
69+
70+
// Check if the class has ObjectType<T> or root type attribute
71+
if (!HasGraphQLTypeAttribute(context, classDeclaration))
72+
{
73+
return;
74+
}
75+
76+
AnalyzeAttributesForMember(classDeclaration.AttributeLists, context);
77+
}
78+
79+
private static void AnalyzeRecordDeclaration(SyntaxNodeAnalysisContext context)
80+
{
81+
var recordDeclaration = (RecordDeclarationSyntax)context.Node;
82+
83+
// Check if the record has ObjectType or root type attribute
84+
if (!HasGraphQLTypeAttribute(context, recordDeclaration))
85+
{
86+
return;
87+
}
88+
89+
AnalyzeAttributesForMember(recordDeclaration.AttributeLists, context);
90+
}
91+
92+
private static void AnalyzeAttributesForMember(
93+
SyntaxList<AttributeListSyntax> attributeLists,
94+
SyntaxNodeAnalysisContext context)
95+
{
96+
foreach (var attributeList in attributeLists)
97+
{
98+
foreach (var attribute in attributeList.Attributes)
99+
{
100+
var symbolInfo = context.SemanticModel.GetSymbolInfo(attribute);
101+
if (symbolInfo.Symbol is not IMethodSymbol attributeSymbol)
102+
{
103+
continue;
104+
}
105+
106+
var attributeType = attributeSymbol.ContainingType;
107+
108+
if (IsMicrosoftAuthorizationAttribute(attributeType))
109+
{
110+
var diagnostic = Diagnostic.Create(
111+
Errors.WrongAuthorizeAttribute,
112+
attribute.GetLocation(),
113+
attributeType.Name);
114+
115+
context.ReportDiagnostic(diagnostic);
116+
}
117+
}
118+
}
119+
}
120+
121+
private static bool HasGraphQLTypeAttribute(
122+
SyntaxNodeAnalysisContext context,
123+
TypeDeclarationSyntax typeDeclaration)
124+
{
125+
return HasObjectTypeAttribute(context, typeDeclaration)
126+
|| HasRootTypeAttribute(context, typeDeclaration);
127+
}
128+
129+
private static bool HasObjectTypeAttribute(
130+
SyntaxNodeAnalysisContext context,
131+
TypeDeclarationSyntax typeDeclaration)
132+
{
133+
foreach (var attributeList in typeDeclaration.AttributeLists)
134+
{
135+
foreach (var attribute in attributeList.Attributes)
136+
{
137+
var symbolInfo = context.SemanticModel.GetSymbolInfo(attribute);
138+
if (symbolInfo.Symbol is not IMethodSymbol attributeSymbol)
139+
{
140+
continue;
141+
}
142+
143+
var attributeType = attributeSymbol.ContainingType;
144+
145+
// Check if this is ObjectTypeAttribute
146+
if (attributeType is INamedTypeSymbol namedAttributeType
147+
&& namedAttributeType.Name == "ObjectTypeAttribute"
148+
&& namedAttributeType.ContainingNamespace?.ToDisplayString() == "HotChocolate.Types")
149+
{
150+
return true;
151+
}
152+
}
153+
}
154+
155+
return false;
156+
}
157+
158+
private static bool HasRootTypeAttribute(
159+
SyntaxNodeAnalysisContext context,
160+
TypeDeclarationSyntax typeDeclaration)
161+
{
162+
if (typeDeclaration.AttributeLists.Count == 0)
163+
{
164+
return false;
165+
}
166+
167+
foreach (var attributeList in typeDeclaration.AttributeLists)
168+
{
169+
foreach (var attribute in attributeList.Attributes)
170+
{
171+
var symbolInfo = context.SemanticModel.GetSymbolInfo(attribute);
172+
if (symbolInfo.Symbol is not IMethodSymbol attributeSymbol)
173+
{
174+
continue;
175+
}
176+
177+
var attributeType = attributeSymbol.ContainingType.ToDisplayString();
178+
179+
if (attributeType.Equals(QueryTypeAttribute, StringComparison.Ordinal)
180+
|| attributeType.Equals(MutationTypeAttribute, StringComparison.Ordinal)
181+
|| attributeType.Equals(SubscriptionTypeAttribute, StringComparison.Ordinal))
182+
{
183+
return true;
184+
}
185+
}
186+
}
187+
188+
return false;
189+
}
190+
191+
private static bool IsMicrosoftAuthorizationAttribute(INamedTypeSymbol attributeType)
192+
{
193+
var attributeName = attributeType.Name;
194+
var namespaceName = attributeType.ContainingNamespace?.ToDisplayString();
195+
196+
if (namespaceName != "Microsoft.AspNetCore.Authorization")
197+
{
198+
return false;
199+
}
200+
201+
return attributeName is "AuthorizeAttribute" or "AllowAnonymousAttribute";
202+
}
203+
}

0 commit comments

Comments
 (0)