Skip to content

Commit 20b7a52

Browse files
committed
chore(tests): backfill missing @Policy tests
1 parent 4ab0580 commit 20b7a52

8 files changed

+912
-1
lines changed

apollo-router/src/plugins/authorization/policy.rs

Lines changed: 134 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ impl traverse::Visitor for PolicyExtractionVisitor<'_> {
144144
node: &executable::Field,
145145
) -> Result<(), BoxError> {
146146
self.get_policies_from_field(field_def);
147-
148147
traverse::field(self, field_def, node)
149148
}
150149

@@ -1334,6 +1333,140 @@ mod tests {
13341333
}
13351334
"#;
13361335

1336+
#[test]
1337+
fn interface_with_implementor_not_defining_field_level_policy() {
1338+
static SCHEMA: &str = r#"
1339+
schema
1340+
@link(url: "https://specs.apollo.dev/link/v1.0")
1341+
@link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)
1342+
@link(url: "https://specs.apollo.dev/policy/v0.1", for: SECURITY)
1343+
{
1344+
query: Query
1345+
}
1346+
directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
1347+
directive @policy(policies: [[String!]!]!) on OBJECT | FIELD_DEFINITION | INTERFACE | SCALAR | ENUM
1348+
scalar link__Import
1349+
enum link__Purpose {
1350+
"""
1351+
`SECURITY` features provide metadata necessary to securely resolve fields.
1352+
"""
1353+
SECURITY
1354+
1355+
"""
1356+
`EXECUTION` features provide metadata necessary for operation execution.
1357+
"""
1358+
EXECUTION
1359+
}
1360+
type Query {
1361+
idList(id: ID!): IList
1362+
}
1363+
interface IList {
1364+
activeId: Int @policy(policies: [["read"]])
1365+
}
1366+
1367+
type BidList implements IList {
1368+
activeId: Int @policy(policies: [["read"]])
1369+
}
1370+
1371+
type PublisherList implements IList {
1372+
activeId: Int @policy(policies: [["read"]])
1373+
}
1374+
1375+
# NOTE: this doesn't have a field-level @policy
1376+
type FrequencyBidList implements IList {
1377+
activeId: Int
1378+
}
1379+
"#;
1380+
1381+
static QUERY: &str = r#"
1382+
query TestIssue {
1383+
idList(id: "1") {
1384+
activeId
1385+
}
1386+
}
1387+
"#;
1388+
let extracted_policies = extract(SCHEMA, QUERY);
1389+
// should have a duplicate test that provides the `read` policy instead of `HashSet::new()`,
1390+
// which should result in `successful_policies` to contain `read` as well
1391+
let read_policy: HashSet<String> = ["read".to_string()].into_iter().collect();
1392+
let (doc, paths) = filter(SCHEMA, QUERY, read_policy);
1393+
insta::assert_snapshot!(TestResult {
1394+
query: QUERY,
1395+
// this should have `read` as the extracted policy
1396+
extracted_policies: &extracted_policies,
1397+
successful_policies: vec!["read".to_string()],
1398+
result: doc,
1399+
paths
1400+
});
1401+
}
1402+
1403+
#[test]
1404+
fn interface_with_implementor_not_defining_type_level_policy() {
1405+
static SCHEMA: &str = r#"
1406+
schema
1407+
@link(url: "https://specs.apollo.dev/link/v1.0")
1408+
@link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)
1409+
@link(url: "https://specs.apollo.dev/policy/v0.1", for: SECURITY)
1410+
{
1411+
query: Query
1412+
}
1413+
directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
1414+
directive @policy(policies: [[String!]!]!) on OBJECT | FIELD_DEFINITION | INTERFACE | SCALAR | ENUM
1415+
scalar link__Import
1416+
enum link__Purpose {
1417+
"""
1418+
`SECURITY` features provide metadata necessary to securely resolve fields.
1419+
"""
1420+
SECURITY
1421+
1422+
"""
1423+
`EXECUTION` features provide metadata necessary for operation execution.
1424+
"""
1425+
EXECUTION
1426+
}
1427+
type Query {
1428+
idList(id: ID!): IList
1429+
}
1430+
interface IList {
1431+
activeId: Int @policy(policies: [["read"]])
1432+
}
1433+
1434+
type BidList implements IList @policy(policies: [["read"]]){
1435+
activeId: Int @policy(policies: [["read"]])
1436+
}
1437+
1438+
type PublisherList implements IList @policy(policies: [["read"]]){
1439+
activeId: Int @policy(policies: [["read"]])
1440+
}
1441+
1442+
# NOTE: this doesn't have a type-level @policy
1443+
type FrequencyBidList implements IList {
1444+
activeId: Int
1445+
}
1446+
"#;
1447+
1448+
static QUERY: &str = r#"
1449+
query TestIssue {
1450+
idList(id: "1") {
1451+
activeId
1452+
}
1453+
}
1454+
"#;
1455+
let extracted_policies = extract(SCHEMA, QUERY);
1456+
// should have a duplicate test that provides the `read` policy instead of `HashSet::new()`,
1457+
// which should result in `successful_policies` to contain `read` as well
1458+
let read_policy: HashSet<String> = ["read".to_string()].into_iter().collect();
1459+
let (doc, paths) = filter(SCHEMA, QUERY, read_policy);
1460+
insta::assert_snapshot!(TestResult {
1461+
query: QUERY,
1462+
// this should have `read` as the extracted policy
1463+
extracted_policies: &extracted_policies,
1464+
successful_policies: vec!["read".to_string()],
1465+
result: doc,
1466+
paths
1467+
});
1468+
}
1469+
13371470
#[test]
13381471
fn interface_field() {
13391472
static QUERY: &str = r#"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
source: apollo-router/src/plugins/authorization/policy.rs
3+
expression: "TestResult\n{\n query: QUERY, extracted_policies: &extracted_policies,\n successful_policies: vec![\"read\".to_string()], result: doc, paths\n}"
4+
---
5+
query:
6+
7+
query TestIssue {
8+
idList(id: "1") {
9+
activeId
10+
}
11+
}
12+
13+
extracted_policies: {"read"}
14+
successful policies: ["read"]
15+
filtered:
16+
17+
paths: ["/idList/activeId"]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
source: apollo-router/src/plugins/authorization/policy.rs
3+
expression: "TestResult\n{\n query: QUERY, extracted_policies: &extracted_policies,\n successful_policies: vec![\"read\".to_string()], result: doc, paths\n}"
4+
---
5+
query:
6+
7+
query TestIssue {
8+
idList(id: "1") {
9+
activeId
10+
}
11+
}
12+
13+
extracted_policies: {"read"}
14+
successful policies: ["read"]
15+
filtered:
16+
17+
paths: ["/idList"]
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
schema
2+
@link(url: "https://specs.apollo.dev/link/v1.0")
3+
@link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)
4+
@link(url: "https://specs.apollo.dev/policy/v0.1", for: SECURITY)
5+
{
6+
query: Query
7+
}
8+
9+
directive @join__graph(name: String!, url: String!) on ENUM_VALUE
10+
directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
11+
directive @join__field(graph: join__Graph!, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
12+
directive @join__type(graph: join__Graph!, key: join__FieldSet) repeatable on OBJECT | INTERFACE
13+
directive @join__owner(graph: join__Graph!) on OBJECT | INTERFACE
14+
directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE
15+
directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
16+
directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
17+
directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION
18+
directive @inaccessible on OBJECT | FIELD_DEFINITION | INTERFACE | UNION
19+
20+
directive @policy(policies: [[String]]) on OBJECT | FIELD_DEFINITION | INTERFACE | SCALAR | ENUM
21+
22+
enum join__Graph {
23+
SUBGRAPH_A @join__graph(name: "subgraph_a", url: "http://localhost:4001/")
24+
}
25+
26+
enum link__Purpose {
27+
"""
28+
`SECURITY` features provide metadata necessary to securely resolve fields.
29+
"""
30+
SECURITY
31+
32+
"""
33+
`EXECUTION` features provide metadata necessary for operation execution.
34+
"""
35+
EXECUTION
36+
}
37+
38+
scalar federation__Scope
39+
scalar join__FieldSet
40+
scalar link__Import
41+
42+
type Query
43+
@join__type(graph: SUBGRAPH_A)
44+
{
45+
private: Private @join__field(graph: SUBGRAPH_A) @policy(policies: [["admin"]])
46+
public: Public @join__field(graph: SUBGRAPH_A)
47+
}
48+
49+
type Private
50+
@join__type(graph: SUBGRAPH_A)
51+
{
52+
id: ID @join__field(graph: SUBGRAPH_A)
53+
}
54+
55+
type Public
56+
@join__type(graph: SUBGRAPH_A)
57+
{
58+
id: ID @join__field(graph: SUBGRAPH_A)
59+
}
60+
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
schema
2+
@link(url: "https://specs.apollo.dev/link/v1.0")
3+
@link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)
4+
@link(url: "https://specs.apollo.dev/policy/v0.1", for: SECURITY)
5+
{
6+
query: Query
7+
}
8+
9+
directive @join__graph(name: String!, url: String!) on ENUM_VALUE
10+
directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
11+
directive @join__field(graph: join__Graph!, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
12+
directive @join__type(graph: join__Graph!, key: join__FieldSet) repeatable on OBJECT | INTERFACE
13+
directive @join__owner(graph: join__Graph!) on OBJECT | INTERFACE
14+
directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE
15+
directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
16+
directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
17+
directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION
18+
directive @inaccessible on OBJECT | FIELD_DEFINITION | INTERFACE | UNION
19+
20+
directive @policy(policies: [[String]]) on OBJECT | FIELD_DEFINITION | INTERFACE | SCALAR | ENUM
21+
22+
enum join__Graph {
23+
SUBGRAPH_A @join__graph(name: "subgraph_a", url: "http://localhost:4001/")
24+
}
25+
26+
enum link__Purpose {
27+
"""
28+
`SECURITY` features provide metadata necessary to securely resolve fields.
29+
"""
30+
SECURITY
31+
32+
"""
33+
`EXECUTION` features provide metadata necessary for operation execution.
34+
"""
35+
EXECUTION
36+
}
37+
38+
scalar federation__Scope
39+
scalar join__FieldSet
40+
scalar link__Import
41+
42+
type Query
43+
@join__type(graph: SUBGRAPH_A)
44+
{
45+
private: Private @join__field(graph: SUBGRAPH_A) @policy(policies: [["admin"]])
46+
public: Public @join__field(graph: SUBGRAPH_A)
47+
opensecret: OpenSecret @join__field(graph: SUBGRAPH_A)
48+
secure: Secure @join__field(graph: SUBGRAPH_A)
49+
}
50+
51+
interface Secure
52+
@join__type(graph: SUBGRAPH_A)
53+
{
54+
id: ID @join__field(graph: SUBGRAPH_A) @policy(policies: [["admin"]])
55+
}
56+
57+
58+
type Private implements Secure
59+
@join__type(graph: SUBGRAPH_A)
60+
@join__implements(graph: SUBGRAPH_A, interface: "Secure")
61+
{
62+
id: ID @policy(policies: [["admin"]]) @join__field(graph: SUBGRAPH_A)
63+
}
64+
65+
# NOTE: despite its name, this _doesn't_ have the `admin` policy
66+
type OpenSecret implements Secure
67+
@join__type(graph: SUBGRAPH_A)
68+
@join__implements(graph: SUBGRAPH_A, interface: "Secure")
69+
{
70+
id: ID @join__field(graph: SUBGRAPH_A)
71+
}
72+
73+
type Public
74+
@join__type(graph: SUBGRAPH_A)
75+
{
76+
id: ID @join__field(graph: SUBGRAPH_A)
77+
}
78+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mod policy;

0 commit comments

Comments
 (0)