feat: add transformer and refactor graphql guards

This commit is contained in:
master 2025-04-23 02:57:22 +08:00
parent 2a5c2b18e7
commit 68aa13e216
4 changed files with 71 additions and 112 deletions

View File

@ -4,11 +4,8 @@ use async_graphql::dynamic::{ResolverContext, ValueAccessor};
use sea_orm::EntityTrait; use sea_orm::EntityTrait;
use seaography::{BuilderContext, FnGuard, GuardAction}; use seaography::{BuilderContext, FnGuard, GuardAction};
use super::util::get_entity_key; use super::util::{get_column_key, get_entity_key};
use crate::{ use crate::auth::{AuthError, AuthUserInfo};
auth::{AuthError, AuthUserInfo},
graphql::util::get_column_key,
};
fn guard_data_object_accessor_with_subscriber_id( fn guard_data_object_accessor_with_subscriber_id(
value: ValueAccessor<'_>, value: ValueAccessor<'_>,
@ -50,27 +47,20 @@ fn guard_data_object_accessor_with_optional_subscriber_id(
} }
} }
fn guard_filter_object_accessor_with_subscriber_id( pub fn guard_entity_with_subscriber_id<T>(_context: &BuilderContext, _column: &T::Column) -> FnGuard
value: ValueAccessor<'_>, where
column_name: &str, T: EntityTrait,
subscriber_id: i32, <T as EntityTrait>::Model: Sync,
) -> async_graphql::Result<()> { {
let obj = value.object()?; Box::new(move |context: &ResolverContext| -> GuardAction {
let subscriber_id_filter_input_value = obj.try_get(column_name)?; match context.ctx.data::<AuthUserInfo>() {
Ok(_) => GuardAction::Allow,
let subscriber_id_filter_input_obj = subscriber_id_filter_input_value.object()?; Err(err) => GuardAction::Block(Some(err.message)),
let subscriber_id_value = subscriber_id_filter_input_obj.try_get("eq")?;
let id = subscriber_id_value.i64()?;
if id == subscriber_id as i64 {
Ok(())
} else {
Err(async_graphql::Error::new("subscriber not match"))
} }
})
} }
pub fn guard_entity_with_subscriber_id<T>(context: &BuilderContext, column: &T::Column) -> FnGuard pub fn guard_field_with_subscriber_id<T>(context: &BuilderContext, column: &T::Column) -> FnGuard
where where
T: EntityTrait, T: EntityTrait,
<T as EntityTrait>::Model: Sync, <T as EntityTrait>::Model: Sync,
@ -95,23 +85,13 @@ where
)); ));
let entity_create_batch_mutation_data_field_name = let entity_create_batch_mutation_data_field_name =
Arc::new(context.entity_create_batch_mutation.data_field.clone()); Arc::new(context.entity_create_batch_mutation.data_field.clone());
let entity_delete_mutation_field_name = Arc::new(format!(
"{}{}",
entity_name,
context.entity_delete_mutation.mutation_suffix.clone()
));
let entity_delete_mutation_filter_field_name =
Arc::new(context.entity_delete_mutation.filter_field.clone());
let entity_update_mutation_field_name = Arc::new(format!( let entity_update_mutation_field_name = Arc::new(format!(
"{}{}", "{}{}",
entity_name, context.entity_update_mutation.mutation_suffix entity_name, context.entity_update_mutation.mutation_suffix
)); ));
let entity_update_mutation_filter_field_name =
Arc::new(context.entity_update_mutation.filter_field.clone());
let entity_update_mutation_data_field_name = let entity_update_mutation_data_field_name =
Arc::new(context.entity_update_mutation.data_field.clone()); Arc::new(context.entity_update_mutation.data_field.clone());
let entity_query_field_name = Arc::new(entity_name);
let entity_query_filter_field_name = Arc::new(context.entity_query_field.filters.clone());
Box::new(move |context: &ResolverContext| -> GuardAction { Box::new(move |context: &ResolverContext| -> GuardAction {
match context.ctx.data::<AuthUserInfo>() { match context.ctx.data::<AuthUserInfo>() {
Ok(user_info) => { Ok(user_info) => {
@ -157,43 +137,7 @@ where
&column_name, &column_name,
) )
}), }),
field if field == entity_delete_mutation_field_name.as_str() => context field if field == entity_update_mutation_field_name.as_str() => {
.args
.try_get(&entity_delete_mutation_filter_field_name)
.and_then(|filter_value| {
guard_filter_object_accessor_with_subscriber_id(
filter_value,
&column_name,
subscriber_id,
)
})
.map_err(|inner_error| {
AuthError::from_graphql_subscribe_id_guard(
inner_error,
context,
&entity_delete_mutation_filter_field_name,
&column_name,
)
}),
field if field == entity_update_mutation_field_name.as_str() => context
.args
.try_get(&entity_update_mutation_filter_field_name)
.and_then(|filter_value| {
guard_filter_object_accessor_with_subscriber_id(
filter_value,
&column_name,
subscriber_id,
)
})
.map_err(|inner_error| {
AuthError::from_graphql_subscribe_id_guard(
inner_error,
context,
&entity_update_mutation_filter_field_name,
&column_name,
)
})
.and_then(|_| {
match context.args.get(&entity_update_mutation_data_field_name) { match context.args.get(&entity_update_mutation_data_field_name) {
Some(data_value) => { Some(data_value) => {
guard_data_object_accessor_with_optional_subscriber_id( guard_data_object_accessor_with_optional_subscriber_id(
@ -212,25 +156,7 @@ where
} }
None => Ok(()), None => Ok(()),
} }
}), }
field if field == entity_query_field_name.as_str() => context
.args
.try_get(&entity_query_filter_field_name)
.and_then(|filter_value| {
guard_filter_object_accessor_with_subscriber_id(
filter_value,
&column_name,
subscriber_id,
)
})
.map_err(|inner_error| {
AuthError::from_graphql_subscribe_id_guard(
inner_error,
context,
&entity_query_filter_field_name,
&column_name,
)
}),
field => Err(AuthError::from_graphql_subscribe_id_guard( field => Err(AuthError::from_graphql_subscribe_id_guard(
async_graphql::Error::new("unsupport graphql field"), async_graphql::Error::new("unsupport graphql field"),
context, context,

View File

@ -4,6 +4,7 @@ pub mod guard;
pub mod schema_root; pub mod schema_root;
pub mod service; pub mod service;
pub mod subscriptions; pub mod subscriptions;
pub mod transformer;
pub mod util; pub mod util;
pub use config::GraphQLConfig; pub use config::GraphQLConfig;

View File

@ -3,12 +3,12 @@ use once_cell::sync::OnceCell;
use sea_orm::{DatabaseConnection, EntityTrait, Iterable}; use sea_orm::{DatabaseConnection, EntityTrait, Iterable};
use seaography::{Builder, BuilderContext, FilterType, FilterTypesMapHelper}; use seaography::{Builder, BuilderContext, FilterType, FilterTypesMapHelper};
use super::transformer::filter_condition_transformer;
use crate::graphql::{ use crate::graphql::{
extentions::AuthExtensionFactory,
filter::{ filter::{
SUBSCRIBER_ID_FILTER_INFO, init_custom_filter_info, subscriber_id_condition_function, SUBSCRIBER_ID_FILTER_INFO, init_custom_filter_info, subscriber_id_condition_function,
}, },
guard::guard_entity_with_subscriber_id, guard::{guard_entity_with_subscriber_id, guard_field_with_subscriber_id},
util::{get_entity_column_key, get_entity_key}, util::{get_entity_column_key, get_entity_key},
}; };
@ -34,9 +34,13 @@ where
let entity_key = get_entity_key::<T>(context); let entity_key = get_entity_key::<T>(context);
let entity_column_key = get_entity_column_key::<T>(context, column); let entity_column_key = get_entity_column_key::<T>(context, column);
context.guards.entity_guards.insert( context.guards.entity_guards.insert(
entity_key, entity_key.clone(),
guard_entity_with_subscriber_id::<T>(context, column), guard_entity_with_subscriber_id::<T>(context, column),
); );
context.guards.field_guards.insert(
entity_column_key.clone(),
guard_field_with_subscriber_id::<T>(context, column),
);
context.filter_types.overwrites.insert( context.filter_types.overwrites.insert(
entity_column_key.clone(), entity_column_key.clone(),
Some(FilterType::Custom( Some(FilterType::Custom(
@ -47,6 +51,10 @@ where
entity_column_key, entity_column_key,
subscriber_id_condition_function::<T>(context, column), subscriber_id_condition_function::<T>(context, column),
); );
context.transformers.filter_conditions_transformers.insert(
entity_key,
filter_condition_transformer::<T>(context, column),
);
} }
pub fn schema( pub fn schema(
@ -156,9 +164,6 @@ pub fn schema(
}; };
schema schema
.data(database) .data(database)
.extension(AuthExtensionFactory {
builder_context: context,
})
.finish() .finish()
.inspect_err(|e| tracing::error!(e = ?e)) .inspect_err(|e| tracing::error!(e = ?e))
} }

View File

@ -0,0 +1,27 @@
use async_graphql::dynamic::ResolverContext;
use sea_orm::{ColumnTrait, Condition, EntityTrait};
use seaography::{BuilderContext, FnFilterConditionsTransformer};
use crate::auth::AuthUserInfo;
pub fn filter_condition_transformer<T>(
_context: &BuilderContext,
column: &T::Column,
) -> FnFilterConditionsTransformer
where
T: EntityTrait,
<T as EntityTrait>::Model: Sync,
{
let column = *column;
Box::new(
move |context: &ResolverContext, condition: Condition| -> Condition {
match context.ctx.data::<AuthUserInfo>() {
Ok(user_info) => {
let subscriber_id = user_info.subscriber_auth.subscriber_id;
condition.add(column.eq(subscriber_id))
}
Err(err) => unreachable!("auth user info must be guarded: {:?}", err),
}
},
)
}