Skip to content

Commit 02ed319

Browse files
Authorize only actual operation (#171)
1 parent 29d27da commit 02ed319

File tree

12 files changed

+199
-63
lines changed

12 files changed

+199
-63
lines changed

.github/codecov.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# https://docs.codecov.com/docs/codecov-yaml
2+
comment:
3+
behavior: new

.github/workflows/codeql-analysis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232

3333
- name: Install dependencies
3434
working-directory: src
35-
run: dotnet restore -p:GraphQLTestVersion=4.4.0
35+
run: dotnet restore -p:GraphQLTestVersion=5.0.0-preview-362
3636

3737
- name: Build solution
3838
working-directory: src

.github/workflows/test.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ jobs:
3030
- ubuntu-latest
3131
- windows-latest
3232
graphqlversion:
33-
- 4.2.0
34-
- 4.4.0
33+
- 5.0.0-preview-362
3534
steps:
3635
- name: Checkout source
3736
uses: actions/checkout@v2

src/BasicSample/BasicSample.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
</ItemGroup>
1212

1313
<ItemGroup>
14-
<PackageReference Include="GraphQL.SystemTextJson" Version="4.2.0" />
14+
<PackageReference Include="GraphQL.SystemTextJson" Version="5.0.0-preview-362" />
1515
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
1616
</ItemGroup>
1717

src/GraphQL.Authorization.ApiTests/GraphQL.Authorization.approved.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ namespace GraphQL.Authorization
5555
public class AuthorizationValidationRule : GraphQL.Validation.IValidationRule
5656
{
5757
public AuthorizationValidationRule(GraphQL.Authorization.IAuthorizationEvaluator evaluator) { }
58-
public System.Threading.Tasks.Task<GraphQL.Validation.INodeVisitor> ValidateAsync(GraphQL.Validation.ValidationContext context) { }
58+
public System.Threading.Tasks.ValueTask<GraphQL.Validation.INodeVisitor> ValidateAsync(GraphQL.Validation.ValidationContext context) { }
5959
}
6060
public class ClaimAuthorizationRequirement : GraphQL.Authorization.IAuthorizationRequirement
6161
{

src/GraphQL.Authorization.Tests/AuthorizationValidationRuleTests.cs

Lines changed: 123 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Generic;
2+
using System.Linq;
23
using GraphQL.Types;
34
using GraphQL.Types.Relay.DataObjects;
45
using Xunit;
@@ -13,11 +14,11 @@ public void class_policy_success()
1314
Settings.AddPolicy("ClassPolicy", builder => builder.RequireClaim("admin"));
1415
Settings.AddPolicy("FieldPolicy", builder => builder.RequireClaim("admin"));
1516

16-
ShouldPassRule(_ =>
17+
ShouldPassRule(config =>
1718
{
18-
_.Query = @"query { post }";
19-
_.Schema = BasicSchema();
20-
_.User = CreatePrincipal(claims: new Dictionary<string, string>
19+
config.Query = @"query { post }";
20+
config.Schema = BasicSchema();
21+
config.User = CreatePrincipal(claims: new Dictionary<string, string>
2122
{
2223
{ "Admin", "true" }
2324
});
@@ -29,10 +30,10 @@ public void class_policy_fail()
2930
{
3031
Settings.AddPolicy("ClassPolicy", builder => builder.RequireClaim("admin"));
3132

32-
ShouldFailRule(_ =>
33+
ShouldFailRule(config =>
3334
{
34-
_.Query = @"query { post }";
35-
_.Schema = BasicSchema();
35+
config.Query = @"query { post }";
36+
config.Schema = BasicSchema();
3637
});
3738
}
3839

@@ -42,11 +43,11 @@ public void field_policy_success()
4243
Settings.AddPolicy("ClassPolicy", builder => builder.RequireClaim("admin"));
4344
Settings.AddPolicy("FieldPolicy", builder => builder.RequireClaim("admin"));
4445

45-
ShouldPassRule(_ =>
46+
ShouldPassRule(config =>
4647
{
47-
_.Query = @"query { post }";
48-
_.Schema = BasicSchema();
49-
_.User = CreatePrincipal(claims: new Dictionary<string, string>
48+
config.Query = @"query { post }";
49+
config.Schema = BasicSchema();
50+
config.User = CreatePrincipal(claims: new Dictionary<string, string>
5051
{
5152
{ "Admin", "true" }
5253
});
@@ -58,10 +59,10 @@ public void field_policy_fail()
5859
{
5960
Settings.AddPolicy("FieldPolicy", builder => builder.RequireClaim("admin"));
6061

61-
ShouldFailRule(_ =>
62+
ShouldFailRule(config =>
6263
{
63-
_.Query = @"query { post }";
64-
_.Schema = BasicSchema();
64+
config.Query = @"query { post }";
65+
config.Schema = BasicSchema();
6566
});
6667
}
6768

@@ -70,11 +71,11 @@ public void nested_type_policy_success()
7071
{
7172
Settings.AddPolicy("PostPolicy", builder => builder.RequireClaim("admin"));
7273

73-
ShouldPassRule(_ =>
74+
ShouldPassRule(config =>
7475
{
75-
_.Query = @"query { post }";
76-
_.Schema = NestedSchema();
77-
_.User = CreatePrincipal(claims: new Dictionary<string, string>
76+
config.Query = @"query { post }";
77+
config.Schema = NestedSchema();
78+
config.User = CreatePrincipal(claims: new Dictionary<string, string>
7879
{
7980
{ "Admin", "true" }
8081
});
@@ -86,10 +87,10 @@ public void nested_type_policy_fail()
8687
{
8788
Settings.AddPolicy("PostPolicy", builder => builder.RequireClaim("admin"));
8889

89-
ShouldFailRule(_ =>
90+
ShouldFailRule(config =>
9091
{
91-
_.Query = @"query { post }";
92-
_.Schema = NestedSchema();
92+
config.Query = @"query { post }";
93+
config.Schema = NestedSchema();
9394
});
9495
}
9596

@@ -98,10 +99,55 @@ public void nested_type_list_policy_fail()
9899
{
99100
Settings.AddPolicy("PostPolicy", builder => builder.RequireClaim("admin"));
100101

101-
ShouldFailRule(_ =>
102+
ShouldFailRule(config =>
102103
{
103-
_.Query = @"query { posts }";
104-
_.Schema = NestedSchema();
104+
config.Query = @"query { posts }";
105+
config.Schema = NestedSchema();
106+
});
107+
}
108+
109+
// https://github.com/graphql-dotnet/authorization/issues/5
110+
[Theory]
111+
[InlineData("c", "query p { posts } query c { comment }")]
112+
[InlineData(null, "query c { comment } query p { posts }")]
113+
public void issue5_should_pass(string operationName, string query)
114+
{
115+
Settings.AddPolicy("PostPolicy", builder => builder.RequireClaim("admin"));
116+
117+
ShouldPassRule(config =>
118+
{
119+
config.OperationName = operationName;
120+
config.Query = query;
121+
config.Schema = NestedSchema();
122+
});
123+
}
124+
125+
// https://github.com/graphql-dotnet/authorization/issues/5
126+
[Theory]
127+
[InlineData("query a { article { id } } query b { article { ...frag } } fragment frag on Article { content }")]
128+
[InlineData("query a { article { ...frag1 author } } query b { article { ...frag2 } } fragment frag1 on Article { id } fragment frag2 on Article { content }")]
129+
public void issue5_with_fragment_should_pass(string query)
130+
{
131+
Settings.AddPolicy("AdminPolicy", builder => builder.RequireClaim("admin"));
132+
133+
ShouldPassRule(config =>
134+
{
135+
config.Query = query;
136+
config.Schema = TypedSchema();
137+
});
138+
}
139+
140+
// https://github.com/graphql-dotnet/authorization/issues/5
141+
[Fact]
142+
public void issue5_with_fragment_should_fail()
143+
{
144+
Settings.AddPolicy("AdminPolicy", builder => builder.RequireClaim("admin"));
145+
146+
ShouldFailRule(config =>
147+
{
148+
config.Query = "query a { article { ...frag } } query b { article { ...frag } } fragment frag on Article { content }";
149+
config.Schema = TypedSchema();
150+
config.ValidateResult = result => result.Errors.Single(x => x.Message == $"You are not authorized to run this query.\nRequired claim 'admin' is not present.");
105151
});
106152
}
107153

@@ -110,10 +156,10 @@ public void nested_type_list_non_null_policy_fail()
110156
{
111157
Settings.AddPolicy("PostPolicy", builder => builder.RequireClaim("admin"));
112158

113-
ShouldFailRule(_ =>
159+
ShouldFailRule(config =>
114160
{
115-
_.Query = @"query { postsNonNull }";
116-
_.Schema = NestedSchema();
161+
config.Query = @"query { postsNonNull }";
162+
config.Schema = NestedSchema();
117163
});
118164
}
119165

@@ -122,11 +168,11 @@ public void passes_with_claim_on_input_type()
122168
{
123169
Settings.AddPolicy("FieldPolicy", builder => builder.RequireClaim("admin"));
124170

125-
ShouldPassRule(_ =>
171+
ShouldPassRule(config =>
126172
{
127-
_.Query = @"query { author(input: { name: ""Quinn"" }) }";
128-
_.Schema = TypedSchema();
129-
_.User = CreatePrincipal(claims: new Dictionary<string, string>
173+
config.Query = @"query { author(input: { name: ""Quinn"" }) }";
174+
config.Schema = TypedSchema();
175+
config.User = CreatePrincipal(claims: new Dictionary<string, string>
130176
{
131177
{ "Admin", "true" }
132178
});
@@ -138,10 +184,10 @@ public void fails_on_missing_claim_on_input_type()
138184
{
139185
Settings.AddPolicy("FieldPolicy", builder => builder.RequireClaim("admin"));
140186

141-
ShouldFailRule(_ =>
187+
ShouldFailRule(config =>
142188
{
143-
_.Query = @"query { author(input: { name: ""Quinn"" }) }";
144-
_.Schema = TypedSchema();
189+
config.Query = @"query { author(input: { name: ""Quinn"" }) }";
190+
config.Schema = TypedSchema();
145191
});
146192
}
147193

@@ -152,11 +198,11 @@ public void passes_with_multiple_policies_on_field_and_single_on_input_type()
152198
Settings.AddPolicy("AdminPolicy", builder => builder.RequireClaim("admin"));
153199
Settings.AddPolicy("ConfidentialPolicy", builder => builder.RequireClaim("admin"));
154200

155-
ShouldPassRule(_ =>
201+
ShouldPassRule(config =>
156202
{
157-
_.Query = @"query { author(input: { name: ""Quinn"" }) project(input: { name: ""TEST"" }) }";
158-
_.Schema = TypedSchema();
159-
_.User = CreatePrincipal(claims: new Dictionary<string, string>
203+
config.Query = @"query { author(input: { name: ""Quinn"" }) project(input: { name: ""TEST"" }) }";
204+
config.Schema = TypedSchema();
205+
config.User = CreatePrincipal(claims: new Dictionary<string, string>
160206
{
161207
{ "Admin", "true" }
162208
});
@@ -166,11 +212,11 @@ public void passes_with_multiple_policies_on_field_and_single_on_input_type()
166212
[Fact]
167213
public void Issue61()
168214
{
169-
ShouldPassRule(_ =>
215+
ShouldPassRule(config =>
170216
{
171-
_.Query = @"query { unknown(obj: {id: 7}) }";
172-
_.Schema = TypedSchema();
173-
_.User = CreatePrincipal(claims: new Dictionary<string, string>
217+
config.Query = @"query { unknown(obj: {id: 7}) }";
218+
config.Schema = TypedSchema();
219+
config.User = CreatePrincipal(claims: new Dictionary<string, string>
174220
{
175221
{ "Admin", "true" }
176222
});
@@ -182,11 +228,11 @@ public void passes_with_policy_on_connection_type()
182228
{
183229
Settings.AddPolicy("ConnectionPolicy", _ => _.RequireClaim("admin"));
184230

185-
ShouldPassRule(_ =>
231+
ShouldPassRule(config =>
186232
{
187-
_.Query = @"query { posts { items { id } } }";
188-
_.Schema = TypedSchema();
189-
_.User = CreatePrincipal(claims: new Dictionary<string, string>
233+
config.Query = @"query { posts { items { id } } }";
234+
config.Schema = TypedSchema();
235+
config.User = CreatePrincipal(claims: new Dictionary<string, string>
190236
{
191237
{ "Admin", "true" }
192238
});
@@ -198,11 +244,11 @@ public void fails_on_missing_claim_on_connection_type()
198244
{
199245
Settings.AddPolicy("ConnectionPolicy", _ => _.RequireClaim("admin"));
200246

201-
ShouldFailRule(_ =>
247+
ShouldFailRule(config =>
202248
{
203-
_.Query = @"query { posts { items { id } } }";
204-
_.Schema = TypedSchema();
205-
_.User = CreatePrincipal();
249+
config.Query = @"query { posts { items { id } } }";
250+
config.Schema = TypedSchema();
251+
config.User = CreatePrincipal();
206252
});
207253
}
208254

@@ -233,6 +279,7 @@ type Query {
233279
post(id: ID!): Post
234280
posts: [Post]
235281
postsNonNull: [Post!]!
282+
comment: String
236283
}
237284
238285
type Post {
@@ -256,6 +303,8 @@ public class NestedQueryWithAttributes
256303
public IEnumerable<Post> Posts() => null;
257304

258305
public IEnumerable<Post> PostsNonNull() => null;
306+
307+
public string Comment() => null;
259308
}
260309

261310
[GraphQLAuthorize("PostPolicy")]
@@ -272,6 +321,25 @@ public PostGraphType()
272321
}
273322
}
274323

324+
public class Article
325+
{
326+
public string Id { get; set; }
327+
328+
public string Author { get; set; }
329+
330+
public string Content { get; set; }
331+
}
332+
333+
public class ArticleGraphType : ObjectGraphType<Article>
334+
{
335+
public ArticleGraphType()
336+
{
337+
Field(p => p.Id);
338+
Field(p => p.Author);
339+
Field(p => p.Content).AuthorizeWith("AdminPolicy");
340+
}
341+
}
342+
275343
public class Author
276344
{
277345
public string Name { get; set; }
@@ -297,6 +365,11 @@ private static ISchema TypedSchema()
297365
resolve: context => "testing"
298366
).AuthorizeWith("AdminPolicy").AuthorizeWith("ConfidentialPolicy");
299367

368+
query.Field<ArticleGraphType>(
369+
"article",
370+
resolve: context => null
371+
);
372+
300373
return new Schema { Query = query };
301374
}
302375

src/GraphQL.Authorization.Tests/GraphQL.Authorization.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
<PropertyGroup>
55
<TargetFrameworks>net5;netcoreapp3.1</TargetFrameworks>
6-
<GraphQLTestVersion>4.2.0</GraphQLTestVersion>
6+
<GraphQLTestVersion>5.0.0-preview-362</GraphQLTestVersion>
77
</PropertyGroup>
88

99
<ItemGroup>

src/GraphQL.Authorization.Tests/ValidationTestBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ private static IValidationResult Validate(ValidationTestConfig config)
6363
var documentBuilder = new GraphQLDocumentBuilder();
6464
var document = documentBuilder.Build(config.Query);
6565
var validator = new DocumentValidator();
66-
return validator.ValidateAsync(config.Schema, document, document.Operations.First().Variables, config.Rules, userContext, config.Inputs).GetAwaiter().GetResult().validationResult;
66+
return validator.ValidateAsync(config.Schema, document, document.Operations.First().Variables, config.Rules, userContext, config.Inputs, config.OperationName).GetAwaiter().GetResult().validationResult;
6767
}
6868

6969
internal static ClaimsPrincipal CreatePrincipal(string authenticationType = null, IDictionary<string, string> claims = null)

src/GraphQL.Authorization.Tests/ValidationTestConfig.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ namespace GraphQL.Authorization.Tests
88
{
99
public class ValidationTestConfig
1010
{
11+
public string OperationName { get; set; }
12+
1113
public string Query { get; set; }
1214

1315
public ISchema Schema { get; set; }

src/GraphQL.Authorization.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
2121
EndProject
2222
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{84514A09-9BB4-4C85-8A8E-92AF5AA26445}"
2323
ProjectSection(SolutionItems) = preProject
24+
..\.github\codecov.yaml = ..\.github\codecov.yaml
2425
..\.github\dependabot.yml = ..\.github\dependabot.yml
2526
..\.github\FUNDING.yml = ..\.github\FUNDING.yml
2627
..\.github\labeler.yml = ..\.github\labeler.yml

0 commit comments

Comments
 (0)