async-graphql/tests/guard.rs

311 lines
7.6 KiB
Rust
Raw Normal View History

2020-05-01 23:57:34 +00:00
use async_graphql::guard::Guard;
use async_graphql::*;
use futures::{Stream, StreamExt};
2020-05-02 03:03:04 +00:00
#[derive(Eq, PartialEq, Copy, Clone)]
enum Role {
Admin,
Guest,
}
struct RoleGuard {
role: Role,
}
2020-05-01 23:57:34 +00:00
2020-05-02 03:03:04 +00:00
#[async_trait::async_trait]
impl Guard for RoleGuard {
async fn check(&self, ctx: &Context<'_>) -> FieldResult<()> {
if ctx.data_opt::<Role>() == Some(&self.role) {
Ok(())
} else {
Err("Forbidden".into())
}
2020-05-01 23:57:34 +00:00
}
2020-05-02 03:03:04 +00:00
}
2020-05-01 23:57:34 +00:00
2020-05-02 03:03:04 +00:00
struct Username(String);
struct UserGuard {
username: String,
}
#[async_trait::async_trait]
impl Guard for UserGuard {
async fn check(&self, ctx: &Context<'_>) -> FieldResult<()> {
if ctx.data_opt::<Username>().map(|name| &name.0).as_deref() == Some(&self.username) {
Ok(())
} else {
Err("Forbidden".into())
2020-05-01 23:57:34 +00:00
}
}
2020-05-02 03:03:04 +00:00
}
2020-05-01 23:57:34 +00:00
2020-05-02 03:03:04 +00:00
#[async_std::test]
pub async fn test_guard() {
#[derive(GQLSimpleObject)]
2020-05-01 23:57:34 +00:00
struct MyObj {
#[field(guard(RoleGuard(role = "Role::Admin")))]
value: i32,
}
struct Query;
#[GQLObject]
2020-05-01 23:57:34 +00:00
impl Query {
#[field(guard(RoleGuard(role = "Role::Admin")))]
2020-05-05 05:02:24 +00:00
async fn value(&self) -> i32 {
1
2020-05-01 23:57:34 +00:00
}
async fn obj(&self) -> MyObj {
MyObj { value: 99 }
}
}
struct Subscription;
#[GQLSubscription]
2020-05-01 23:57:34 +00:00
impl Subscription {
#[field(guard(RoleGuard(role = "Role::Admin")))]
2020-05-05 05:02:24 +00:00
async fn values(&self) -> impl Stream<Item = i32> {
2020-05-01 23:57:34 +00:00
futures::stream::iter(vec![1, 2, 3])
}
}
let schema = Schema::new(Query, EmptyMutation, Subscription);
let query = "{ obj { value } }";
assert_eq!(
2020-09-10 11:35:48 +00:00
schema
.execute(Request::new(query).data(Role::Admin))
2020-05-01 23:57:34 +00:00
.await
.data,
serde_json::json!({
"obj": {"value": 99}
})
);
let query = "{ obj { value } }";
assert_eq!(
2020-09-10 11:35:48 +00:00
schema
.execute(Request::new(query).data(Role::Guest))
2020-05-01 23:57:34 +00:00
.await
.into_result()
2020-05-01 23:57:34 +00:00
.unwrap_err(),
Error::Query {
pos: Pos { line: 1, column: 9 },
path: Some(serde_json::json!(["obj", "value"])),
err: QueryError::FieldError {
err: "Forbidden".to_string(),
extended_error: None,
},
}
);
2020-05-05 05:02:24 +00:00
let query = "{ value }";
2020-05-01 23:57:34 +00:00
assert_eq!(
2020-09-10 11:35:48 +00:00
schema
.execute(Request::new(query).data(Role::Admin))
2020-05-01 23:57:34 +00:00
.await
.data,
serde_json::json!({
2020-05-05 05:02:24 +00:00
"value": 1,
2020-05-01 23:57:34 +00:00
})
);
2020-05-05 05:02:24 +00:00
let query = "{ value }";
2020-05-01 23:57:34 +00:00
assert_eq!(
2020-09-10 11:35:48 +00:00
schema
.execute(Request::new(query).data(Role::Guest))
2020-05-01 23:57:34 +00:00
.await
.into_result()
2020-05-01 23:57:34 +00:00
.unwrap_err(),
Error::Query {
pos: Pos { line: 1, column: 3 },
2020-05-05 05:02:24 +00:00
path: Some(serde_json::json!(["value"])),
2020-05-01 23:57:34 +00:00
err: QueryError::FieldError {
err: "Forbidden".to_string(),
extended_error: None,
},
}
);
assert_eq!(
schema
2020-09-10 11:35:48 +00:00
.execute_stream(Request::new("subscription { values }").data(Role::Admin))
.map(|item| item.data)
2020-05-01 23:57:34 +00:00
.collect::<Vec<_>>()
.await,
vec![
2020-09-10 11:35:48 +00:00
serde_json::json! ({"values": 1}),
serde_json::json! ({"values": 2}),
serde_json::json! ({"values": 3})
2020-05-01 23:57:34 +00:00
]
);
assert_eq!(
schema
2020-09-10 11:35:48 +00:00
.execute_stream(Request::new("subscription { values }").data(Role::Guest))
2020-09-11 08:05:21 +00:00
.boxed()
.next()
2020-05-01 23:57:34 +00:00
.await
.unwrap()
.error
.unwrap(),
2020-05-01 23:57:34 +00:00
Error::Query {
pos: Pos {
line: 1,
column: 16
},
2020-05-05 05:02:24 +00:00
path: Some(serde_json::json!(["values"])),
2020-05-01 23:57:34 +00:00
err: QueryError::FieldError {
err: "Forbidden".to_string(),
extended_error: None,
},
}
);
}
2020-05-02 03:03:04 +00:00
#[async_std::test]
pub async fn test_multiple_guards() {
#[derive(GQLSimpleObject)]
2020-05-02 03:03:04 +00:00
struct Query {
#[field(guard(RoleGuard(role = "Role::Admin"), UserGuard(username = r#""test""#)))]
value: i32,
}
let schema = Schema::new(Query { value: 10 }, EmptyMutation, EmptySubscription);
let query = "{ value }";
assert_eq!(
2020-09-10 11:35:48 +00:00
schema
.execute(
Request::new(query)
.data(Role::Admin)
.data(Username("test".to_string()))
)
2020-05-02 03:03:04 +00:00
.await
.data,
serde_json::json!({"value": 10})
);
let query = "{ value }";
assert_eq!(
2020-09-10 11:35:48 +00:00
schema
.execute(
Request::new(query)
.data(Role::Guest)
.data(Username("test".to_string()))
)
2020-05-02 03:03:04 +00:00
.await
.into_result()
2020-05-02 03:03:04 +00:00
.unwrap_err(),
Error::Query {
pos: Pos { line: 1, column: 3 },
path: Some(serde_json::json!(["value"])),
err: QueryError::FieldError {
err: "Forbidden".to_string(),
extended_error: None,
},
}
);
let query = "{ value }";
assert_eq!(
2020-09-10 11:35:48 +00:00
schema
.execute(
Request::new(query)
.data(Role::Admin)
.data(Username("test1".to_string()))
)
2020-05-02 03:03:04 +00:00
.await
.into_result()
2020-05-02 03:03:04 +00:00
.unwrap_err(),
Error::Query {
pos: Pos { line: 1, column: 3 },
path: Some(serde_json::json!(["value"])),
err: QueryError::FieldError {
err: "Forbidden".to_string(),
extended_error: None,
},
}
);
let query = "{ value }";
assert_eq!(
2020-09-10 11:35:48 +00:00
schema
.execute(
Request::new(query)
.data(Role::Guest)
.data(Username("test1".to_string()))
)
2020-05-02 03:03:04 +00:00
.await
.into_result()
2020-05-02 03:03:04 +00:00
.unwrap_err(),
Error::Query {
pos: Pos { line: 1, column: 3 },
path: Some(serde_json::json!(["value"])),
err: QueryError::FieldError {
err: "Forbidden".to_string(),
extended_error: None,
},
}
);
}
#[async_std::test]
pub async fn test_guard_forward_arguments() {
2020-06-03 06:50:06 +00:00
struct UserGuard {
id: ID,
}
#[async_trait::async_trait]
2020-06-03 06:50:06 +00:00
impl Guard for UserGuard {
async fn check(&self, ctx: &Context<'_>) -> FieldResult<()> {
2020-06-03 06:50:06 +00:00
if ctx.data_opt::<ID>() != Some(&self.id) {
Err("Forbidden".into())
} else {
Ok(())
}
}
}
struct QueryRoot;
#[GQLObject]
impl QueryRoot {
#[field(guard(UserGuard(id = "@id")))]
async fn user(&self, id: ID) -> ID {
id
}
}
let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
let query = r#"{ user(id: "abc") }"#;
assert_eq!(
2020-09-10 11:35:48 +00:00
schema
.execute(Request::new(query).data(ID::from("abc")))
.await
.data,
serde_json::json!({"user": "abc"})
);
let query = r#"{ user(id: "abc") }"#;
assert_eq!(
2020-09-10 11:35:48 +00:00
schema
.execute(Request::new(query).data(ID::from("aaa")))
.await
.into_result()
.unwrap_err(),
Error::Query {
pos: Pos { line: 1, column: 3 },
path: Some(serde_json::json!(["user"])),
err: QueryError::FieldError {
err: "Forbidden".to_string(),
extended_error: None,
},
}
);
}