Skip to content

Commit 1e40248

Browse files
committed
rbac: add _seaography_user_role_permission
1 parent bc495b9 commit 1e40248

File tree

7 files changed

+157
-3
lines changed

7 files changed

+157
-3
lines changed

.github/workflows/tests.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,10 @@ jobs:
8585
cargo test --test custom_query_tests
8686
cargo test --test custom_mutation_tests
8787
cargo test --test query_tests --features=rbac
88+
cargo test --test rbac_tests --features=rbac
8889
cargo test --test plural_query_tests --features=field-pluralize
8990
cargo test --test entity_filter_tests --features=field-pluralize
90-
rm tests/custom_mutation_tests.rs tests/custom_query_tests.rs tests/plural_query_tests.rs
91+
rm tests/custom_mutation_tests.rs tests/custom_query_tests.rs tests/plural_query_tests.rs tests/rbac_tests.rs
9192
- name: Remove generated folder
9293
run: rm -rf ./examples/sqlite/src
9394
- name: Copy sample database

examples/sqlite/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,11 @@ field-pluralize = ["seaography/field-pluralize"]
3232
name = "plural_query_tests"
3333
path = "tests/plural_query_tests.rs"
3434
required-features = ["field-pluralize"]
35+
36+
[[test]]
37+
name = "rbac_tests"
38+
path = "tests/rbac_tests.rs"
39+
required-features = ["rbac"]
40+
41+
[patch.crates-io]
42+
sea-orm = { git = "https://github.com/SeaQL/sea-orm", branch = "master" }

examples/sqlite/src/main.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use async_graphql::{
66
use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};
77
use dotenv::dotenv;
88
use sea_orm::Database;
9-
use seaography::{async_graphql, lazy_static};
9+
use seaography::{async_graphql, lazy_static, DatabaseContext};
1010
use std::env;
1111

1212
lazy_static::lazy_static! {
@@ -42,7 +42,8 @@ async fn main() -> std::io::Result<()> {
4242
.init();
4343
let database = Database::connect(&*DATABASE_URL)
4444
.await
45-
.expect("Fail to initialize database connection");
45+
.expect("Fail to initialize database connection")
46+
.unrestricted();
4647
let schema =
4748
seaography_sqlite_example::query_root::schema(database, *DEPTH_LIMIT, *COMPLEXITY_LIMIT)
4849
.unwrap();
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use async_graphql::{dynamic::*, Response};
2+
use sea_orm::Database;
3+
use seaography::{async_graphql, DatabaseContext};
4+
5+
pub async fn get_schema() -> Schema {
6+
let database = Database::connect("sqlite://sakila.db").await.unwrap();
7+
let schema =
8+
seaography_sqlite_example::query_root::schema(database.unrestricted(), None, None).unwrap();
9+
10+
schema
11+
}
12+
13+
pub fn assert_eq(a: Response, b: &str) {
14+
assert_eq!(
15+
a.data.into_json().unwrap(),
16+
serde_json::from_str::<serde_json::Value>(b).unwrap()
17+
)
18+
}
19+
20+
#[tokio::test]
21+
async fn test_user_role_permission() {
22+
let schema = get_schema().await;
23+
24+
assert_eq(
25+
schema
26+
.execute(
27+
r#"
28+
{
29+
_seaography_current_user_role_permission {
30+
role {
31+
role
32+
}
33+
permissions {
34+
resource {
35+
schema
36+
table
37+
}
38+
permission {
39+
action
40+
}
41+
}
42+
}
43+
}
44+
"#,
45+
)
46+
.await,
47+
r#"
48+
{
49+
"_seaography_current_user_role_permission": {
50+
"role": {
51+
"role": "unrestricted"
52+
},
53+
"permissions": [
54+
{
55+
"resource": {
56+
"schema": null,
57+
"table": "*"
58+
},
59+
"permission": {
60+
"action": "*"
61+
}
62+
}
63+
]
64+
}
65+
}
66+
"#,
67+
)
68+
}

src/builder.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,11 +369,27 @@ impl Builder {
369369
self.queries.push(field);
370370
}
371371

372+
#[cfg(feature = "rbac")]
373+
fn register_rbac(&mut self) {
374+
use crate::rbac::*;
375+
376+
self.register_custom_output::<UserRolePermissions>();
377+
self.register_custom_output::<ResourcePermission>();
378+
self.register_custom_entity::<role::Entity>();
379+
self.register_custom_entity::<resource::Entity>();
380+
self.register_custom_entity::<permission::Entity>();
381+
382+
self.register_custom_query::<Queries>();
383+
}
384+
372385
/// used to consume the builder context and generate a ready to be completed GraphQL schema
373386
pub fn schema_builder(mut self) -> SchemaBuilder {
374387
#[cfg(feature = "schema-meta")]
375388
self.register_schema_meta();
376389

390+
#[cfg(feature = "rbac")]
391+
self.register_rbac();
392+
377393
let query = self.query;
378394
let mutation = self.mutation;
379395
let subscription = self.subscription;

src/rbac.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
use sea_orm::{DatabaseConnection, DbErr, StatementBuilder};
22

3+
#[cfg(feature = "rbac")]
4+
mod operations;
5+
#[cfg(feature = "rbac")]
6+
pub use operations::*;
7+
38
#[derive(Default)]
49
pub struct UserContext {
510
pub user_id: i64,

src/rbac/operations.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use crate as seaography;
2+
use async_graphql::{dynamic::ResolverContext, Result as GqlResult};
3+
pub use sea_orm::rbac::entity::{permission, resource, role};
4+
use sea_orm::DatabaseConnection;
5+
use seaography::{
6+
macros::{CustomOperation, CustomOutput},
7+
BuilderContext, DatabaseContext, UserContext,
8+
};
9+
10+
// Users can't customize this context
11+
lazy_static::lazy_static! { static ref CONTEXT: BuilderContext = BuilderContext::default(); }
12+
13+
#[derive(Clone, CustomOutput)]
14+
#[seaography(prefix = "SeaOrm", suffix = "")]
15+
pub struct UserRolePermissions {
16+
pub role: role::Model,
17+
pub permissions: Vec<ResourcePermission>,
18+
}
19+
20+
#[derive(Clone, CustomOutput)]
21+
#[seaography(prefix = "SeaOrm", suffix = "")]
22+
pub struct ResourcePermission {
23+
pub resource: resource::Model,
24+
pub permission: permission::Model,
25+
}
26+
27+
#[allow(dead_code)]
28+
#[derive(CustomOperation)]
29+
pub struct Queries {
30+
_seaography_current_user_role_permission: fn() -> UserRolePermissions,
31+
}
32+
33+
impl Queries {
34+
async fn _seaography_current_user_role_permission(
35+
ctx: &ResolverContext<'_>,
36+
) -> GqlResult<UserRolePermissions> {
37+
let db = &ctx
38+
.data::<DatabaseConnection>()?
39+
.restricted(ctx.data_opt::<UserContext>())?;
40+
41+
let role = db.user_role_permissions()?;
42+
43+
Ok(UserRolePermissions {
44+
role: role.role,
45+
permissions: role
46+
.permissions
47+
.into_iter()
48+
.map(|(resource, permission)| ResourcePermission {
49+
resource,
50+
permission,
51+
})
52+
.collect(),
53+
})
54+
}
55+
}

0 commit comments

Comments
 (0)