implement access rights for view models
This commit is contained in:
parent
c0291efa28
commit
4b53e45875
@ -22,31 +22,11 @@ pub fn derive_actix_admin_select_list(input: proc_macro::TokenStream) -> proc_ma
|
|||||||
get_select_list(input)
|
get_select_list(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_derive(DeriveActixAdminModel, attributes(actix_admin))]
|
#[proc_macro_derive(DeriveActixAdmin, attributes(actix_admin))]
|
||||||
pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_actix_admin(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let fields = get_fields_for_tokenstream(input);
|
|
||||||
|
|
||||||
let field_names = get_actix_admin_fields(&fields);
|
|
||||||
let field_html_input_type = get_actix_admin_fields_html_input(&fields);
|
|
||||||
let field_select_list = get_actix_admin_fields_select_list(&fields);
|
|
||||||
let is_option_list = get_actix_admin_fields_is_option_list(&fields);
|
|
||||||
let name_primary_field_str = get_primary_key_field_name(&fields);
|
|
||||||
let fields_for_create_model = get_fields_for_create_model(&fields);
|
|
||||||
let fields_for_edit_model = get_fields_for_edit_model(&fields);
|
|
||||||
let fields_for_from_model = get_fields_for_from_model(&fields);
|
|
||||||
let field_for_primary_key = get_field_for_primary_key(&fields);
|
|
||||||
let fields_for_validate_model = get_fields_for_validate_model(&fields);
|
|
||||||
let fields_searchable = get_actix_admin_fields_searchable(&fields);
|
|
||||||
let has_searchable_fields = fields_searchable.len() > 0;
|
|
||||||
let fields_type_path = get_actix_admin_fields_type_path_string(&fields);
|
|
||||||
|
|
||||||
let select_lists = get_select_lists(&fields);
|
|
||||||
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::iter::zip;
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use actix_web::{web, HttpResponse, HttpRequest, Error};
|
|
||||||
use actix_admin::prelude::*;
|
use actix_admin::prelude::*;
|
||||||
use sea_orm::ActiveValue::Set;
|
use sea_orm::ActiveValue::Set;
|
||||||
use sea_orm::{ConnectOptions, DatabaseConnection};
|
use sea_orm::{ConnectOptions, DatabaseConnection};
|
||||||
@ -54,8 +34,35 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use sea_orm::EntityTrait;
|
use sea_orm::EntityTrait;
|
||||||
use itertools::izip;
|
use itertools::izip;
|
||||||
use quote::quote;
|
use actix_session::{Session};
|
||||||
|
};
|
||||||
|
proc_macro::TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(DeriveActixAdminViewModelAccess, attributes(actix_admin))]
|
||||||
|
pub fn derive_actix_admin_view_model_access(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let expanded = quote! {
|
||||||
|
impl ActixAdminViewModelAccessTrait for Entity {
|
||||||
|
fn user_can_access(session: &Session) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
proc_macro::TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(DeriveActixAdminViewModel, attributes(actix_admin))]
|
||||||
|
pub fn derive_actix_admin_view_model(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let fields = get_fields_for_tokenstream(input);
|
||||||
|
|
||||||
|
let name_primary_field_str = get_primary_key_field_name(&fields);
|
||||||
|
let fields_for_edit_model = get_fields_for_edit_model(&fields);
|
||||||
|
let fields_searchable = get_actix_admin_fields_searchable(&fields);
|
||||||
|
let has_searchable_fields = fields_searchable.len() > 0;
|
||||||
|
|
||||||
|
let select_lists = get_select_lists(&fields);
|
||||||
|
|
||||||
|
let expanded = quote! {
|
||||||
impl From<Entity> for ActixAdminViewModel {
|
impl From<Entity> for ActixAdminViewModel {
|
||||||
fn from(entity: Entity) -> Self {
|
fn from(entity: Entity) -> Self {
|
||||||
ActixAdminViewModel {
|
ActixAdminViewModel {
|
||||||
@ -67,29 +74,6 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Model> for ActixAdminModel {
|
|
||||||
fn from(model: Model) -> Self {
|
|
||||||
ActixAdminModel {
|
|
||||||
#field_for_primary_key,
|
|
||||||
values: hashmap![
|
|
||||||
#(#fields_for_from_model),*
|
|
||||||
],
|
|
||||||
errors: HashMap::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ActixAdminModel> for ActiveModel {
|
|
||||||
fn from(model: ActixAdminModel) -> Self {
|
|
||||||
ActiveModel
|
|
||||||
{
|
|
||||||
#(#fields_for_create_model),*
|
|
||||||
,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl ActixAdminViewModelTrait for Entity {
|
impl ActixAdminViewModelTrait for Entity {
|
||||||
async fn list(db: &DatabaseConnection, page: usize, entities_per_page: usize, search: &String) -> (usize, Vec<ActixAdminModel>) {
|
async fn list(db: &DatabaseConnection, page: usize, entities_per_page: usize, search: &String) -> (usize, Vec<ActixAdminModel>) {
|
||||||
@ -151,6 +135,49 @@ pub fn derive_crud_fns(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
|||||||
Entity.table_name().to_string()
|
Entity.table_name().to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
proc_macro::TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(DeriveActixAdminModel, attributes(actix_admin))]
|
||||||
|
pub fn derive_actix_admin_model(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let fields = get_fields_for_tokenstream(input);
|
||||||
|
|
||||||
|
let field_names = get_actix_admin_fields(&fields);
|
||||||
|
let field_html_input_type = get_actix_admin_fields_html_input(&fields);
|
||||||
|
let field_select_list = get_actix_admin_fields_select_list(&fields);
|
||||||
|
let is_option_list = get_actix_admin_fields_is_option_list(&fields);
|
||||||
|
let fields_for_create_model = get_fields_for_create_model(&fields);
|
||||||
|
let fields_for_from_model = get_fields_for_from_model(&fields);
|
||||||
|
let field_for_primary_key = get_field_for_primary_key(&fields);
|
||||||
|
let fields_for_validate_model = get_fields_for_validate_model(&fields);
|
||||||
|
let fields_searchable = get_actix_admin_fields_searchable(&fields);
|
||||||
|
let fields_type_path = get_actix_admin_fields_type_path_string(&fields);
|
||||||
|
|
||||||
|
let expanded = quote! {
|
||||||
|
impl From<Model> for ActixAdminModel {
|
||||||
|
fn from(model: Model) -> Self {
|
||||||
|
ActixAdminModel {
|
||||||
|
#field_for_primary_key,
|
||||||
|
values: hashmap![
|
||||||
|
#(#fields_for_from_model),*
|
||||||
|
],
|
||||||
|
errors: HashMap::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ActixAdminModel> for ActiveModel {
|
||||||
|
fn from(model: ActixAdminModel) -> Self {
|
||||||
|
ActiveModel
|
||||||
|
{
|
||||||
|
#(#fields_for_create_model),*
|
||||||
|
,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl ActixAdminModelTrait for Entity {
|
impl ActixAdminModelTrait for Entity {
|
||||||
|
@ -12,7 +12,7 @@ pub struct ActixAdminBuilder {
|
|||||||
|
|
||||||
pub trait ActixAdminBuilderTrait {
|
pub trait ActixAdminBuilderTrait {
|
||||||
fn new(configuration: ActixAdminConfiguration) -> Self;
|
fn new(configuration: ActixAdminConfiguration) -> Self;
|
||||||
fn add_entity<T: ActixAdminAppDataTrait + 'static, E: ActixAdminViewModelTrait + 'static>(
|
fn add_entity<T: ActixAdminAppDataTrait + 'static, E: ActixAdminViewModelTrait + ActixAdminViewModelAccessTrait + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
view_model: &ActixAdminViewModel,
|
view_model: &ActixAdminViewModel,
|
||||||
);
|
);
|
||||||
@ -32,7 +32,7 @@ impl ActixAdminBuilderTrait for ActixAdminBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_entity<T: ActixAdminAppDataTrait + 'static, E: ActixAdminViewModelTrait + 'static>(
|
fn add_entity<T: ActixAdminAppDataTrait + 'static, E: ActixAdminViewModelTrait + ActixAdminViewModelAccessTrait + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
view_model: &ActixAdminViewModel,
|
view_model: &ActixAdminViewModel,
|
||||||
) {
|
) {
|
||||||
|
12
src/lib.rs
12
src/lib.rs
@ -13,8 +13,8 @@ pub mod builder;
|
|||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::builder::{ ActixAdminBuilder, ActixAdminBuilderTrait};
|
pub use crate::builder::{ ActixAdminBuilder, ActixAdminBuilderTrait};
|
||||||
pub use crate::model::{ ActixAdminModel, ActixAdminModelTrait};
|
pub use crate::model::{ ActixAdminModel, ActixAdminModelTrait};
|
||||||
pub use crate::view_model::{ ActixAdminViewModel, ActixAdminViewModelTrait, ActixAdminViewModelField, ActixAdminViewModelFieldType };
|
pub use crate::view_model::{ ActixAdminViewModel, ActixAdminViewModelTrait, ActixAdminViewModelAccessTrait, ActixAdminViewModelField, ActixAdminViewModelFieldType };
|
||||||
pub use actix_admin_macros::{ DeriveActixAdminModel, DeriveActixAdminSelectList };
|
pub use actix_admin_macros::{ DeriveActixAdmin, DeriveActixAdminModel, DeriveActixAdminViewModel, DeriveActixAdminSelectList, DeriveActixAdminViewModelAccess };
|
||||||
pub use crate::{ ActixAdminAppDataTrait, ActixAdmin, ActixAdminConfiguration };
|
pub use crate::{ ActixAdminAppDataTrait, ActixAdmin, ActixAdminConfiguration };
|
||||||
pub use crate::{ hashmap, ActixAdminSelectListTrait };
|
pub use crate::{ hashmap, ActixAdminSelectListTrait };
|
||||||
}
|
}
|
||||||
@ -93,17 +93,17 @@ pub trait ActixAdminSelectListTrait {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone)]
|
||||||
pub struct ActixAdminConfiguration {
|
pub struct ActixAdminConfiguration {
|
||||||
pub enable_auth: bool,
|
pub enable_auth: bool,
|
||||||
pub user_is_logged_in: Option<fn(Session) -> bool>,
|
pub user_is_logged_in: Option<for<'a> fn(&'a Session) -> bool>,
|
||||||
pub login_link: String,
|
pub login_link: String,
|
||||||
pub logout_link: String
|
pub logout_link: String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone)]
|
||||||
pub struct ActixAdmin {
|
pub struct ActixAdmin {
|
||||||
pub entity_names: Vec<String>,
|
pub entity_names: Vec<String>,
|
||||||
pub view_models: HashMap<String, ActixAdminViewModel>,
|
pub view_models: HashMap<String, ActixAdminViewModel>,
|
||||||
pub configuration: ActixAdminConfiguration,
|
pub configuration: ActixAdminConfiguration
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,9 @@ use actix_session::{Session};
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use crate::TERA;
|
use crate::TERA;
|
||||||
use super::add_auth_context;
|
use super::{ add_auth_context, user_can_access_page, render_unauthorized};
|
||||||
|
|
||||||
|
pub async fn create_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait + ActixAdminViewModelAccessTrait>(
|
||||||
pub async fn create_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
|
|
||||||
session: Session,
|
session: Session,
|
||||||
_req: HttpRequest,
|
_req: HttpRequest,
|
||||||
data: web::Data<T>,
|
data: web::Data<T>,
|
||||||
@ -17,10 +16,10 @@ pub async fn create_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
|
|||||||
let db = &data.get_db();
|
let db = &data.get_db();
|
||||||
let model = ActixAdminModel::create_empty();
|
let model = ActixAdminModel::create_empty();
|
||||||
|
|
||||||
create_or_edit_get::<T, E>(session, &data, db, model).await
|
create_or_edit_get::<T, E>(&session, &data, db, model).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn edit_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
|
pub async fn edit_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait + ActixAdminViewModelAccessTrait>(
|
||||||
session: Session,
|
session: Session,
|
||||||
_req: HttpRequest,
|
_req: HttpRequest,
|
||||||
data: web::Data<T>,
|
data: web::Data<T>,
|
||||||
@ -30,27 +29,27 @@ pub async fn edit_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
|
|||||||
let db = &data.get_db();
|
let db = &data.get_db();
|
||||||
let model = E::get_entity(db, id.into_inner()).await;
|
let model = E::get_entity(db, id.into_inner()).await;
|
||||||
|
|
||||||
create_or_edit_get::<T, E>(session, &data, db, model).await
|
create_or_edit_get::<T, E>(&session, &data, db, model).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_or_edit_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(session: Session, data: &web::Data<T>, db: &sea_orm::DatabaseConnection, model: ActixAdminModel) -> Result<HttpResponse, Error>{
|
async fn create_or_edit_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait + ActixAdminViewModelAccessTrait>(session: &Session, data: &web::Data<T>, db: &sea_orm::DatabaseConnection, model: ActixAdminModel) -> Result<HttpResponse, Error>{
|
||||||
|
let actix_admin = &data.get_actix_admin();
|
||||||
|
let mut ctx = Context::new();
|
||||||
|
add_auth_context(&session, actix_admin, &mut ctx);
|
||||||
|
let entity_names = &actix_admin.entity_names;
|
||||||
|
ctx.insert("entity_names", entity_names);
|
||||||
let entity_name = E::get_entity_name();
|
let entity_name = E::get_entity_name();
|
||||||
let entity_names = &data.get_actix_admin().entity_names;
|
|
||||||
|
|
||||||
let actix_admin = data.get_actix_admin();
|
if !user_can_access_page::<E>(&session, actix_admin) {
|
||||||
|
return render_unauthorized(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
let view_model = actix_admin.view_models.get(&entity_name).unwrap();
|
let view_model = actix_admin.view_models.get(&entity_name).unwrap();
|
||||||
|
|
||||||
let mut ctx = Context::new();
|
|
||||||
ctx.insert("entity_names", &entity_names);
|
|
||||||
ctx.insert("view_model", &view_model);
|
ctx.insert("view_model", &view_model);
|
||||||
ctx.insert("select_lists", &E::get_select_lists(db).await);
|
ctx.insert("select_lists", &E::get_select_lists(db).await);
|
||||||
ctx.insert("list_link", &E::get_list_link(&entity_name));
|
ctx.insert("list_link", &E::get_list_link(&entity_name));
|
||||||
ctx.insert("model", &model);
|
ctx.insert("model", &model);
|
||||||
|
|
||||||
add_auth_context(session, actix_admin, &mut ctx);
|
|
||||||
// TODO: show 404 if user is not logged in but auth enabled
|
|
||||||
|
|
||||||
let body = TERA
|
let body = TERA
|
||||||
.render("create_or_edit.html", &ctx)
|
.render("create_or_edit.html", &ctx)
|
||||||
.map_err(|err| error::ErrorInternalServerError(err))?;
|
.map_err(|err| error::ErrorInternalServerError(err))?;
|
||||||
|
@ -1,50 +1,45 @@
|
|||||||
use actix_web::http::header;
|
use actix_web::http::header;
|
||||||
use actix_web::{web, error, Error, HttpRequest, HttpResponse};
|
use actix_web::{web, error, Error, HttpResponse};
|
||||||
use tera::{Context};
|
use tera::{Context};
|
||||||
use actix_session::{Session};
|
use actix_session::{Session};
|
||||||
use crate::TERA;
|
use crate::TERA;
|
||||||
use actix_multipart::Multipart;
|
use actix_multipart::Multipart;
|
||||||
|
use super::{ user_can_access_page, render_unauthorized};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub async fn create_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
|
pub async fn create_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait + ActixAdminViewModelAccessTrait>(
|
||||||
session: Session,
|
session: Session,
|
||||||
_req: HttpRequest,
|
|
||||||
data: web::Data<T>,
|
data: web::Data<T>,
|
||||||
payload: Multipart,
|
payload: Multipart,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let db = &data.get_db();
|
create_or_edit_post::<T, E>(&session, &data, payload, None).await
|
||||||
let mut model = ActixAdminModel::create_from_payload(payload).await.unwrap();
|
|
||||||
model = E::create_entity(db, model).await;
|
|
||||||
|
|
||||||
create_or_edit_post::<T, E>(session, &data, db, model).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn edit_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
|
pub async fn edit_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait + ActixAdminViewModelAccessTrait>(
|
||||||
session: Session,
|
session: Session,
|
||||||
_req: HttpRequest,
|
|
||||||
data: web::Data<T>,
|
data: web::Data<T>,
|
||||||
payload: Multipart,
|
payload: Multipart,
|
||||||
id: web::Path<i32>
|
id: web::Path<i32>
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let db = &data.get_db();
|
create_or_edit_post::<T, E>(&session, &data, payload, Some(id.into_inner())).await
|
||||||
let mut model = ActixAdminModel::create_from_payload(payload).await.unwrap();
|
|
||||||
model = E::edit_entity(db, id.into_inner(), model).await;
|
|
||||||
|
|
||||||
create_or_edit_post::<T, E>(session, &data, db, model).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_or_edit_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(_session: Session, data: &web::Data<T>, db: &sea_orm::DatabaseConnection, model: ActixAdminModel) -> Result<HttpResponse, Error> {
|
async fn create_or_edit_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait + ActixAdminViewModelAccessTrait>(session: &Session, data: &web::Data<T>, payload: Multipart, id: Option<i32>) -> Result<HttpResponse, Error> {
|
||||||
let entity_name = E::get_entity_name();
|
|
||||||
let entity_names = &data.get_actix_admin().entity_names;
|
|
||||||
let actix_admin = data.get_actix_admin();
|
let actix_admin = data.get_actix_admin();
|
||||||
let view_model = actix_admin.view_models.get(&entity_name).unwrap();
|
if !user_can_access_page::<E>(&session, actix_admin) {
|
||||||
|
let mut ctx = Context::new();
|
||||||
|
ctx.insert("render_partial", &true);
|
||||||
|
return render_unauthorized(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: verify is user is logged in and can delete entity
|
let db = &data.get_db();
|
||||||
|
let entity_name = E::get_entity_name();
|
||||||
|
let view_model = actix_admin.view_models.get(&entity_name).unwrap();
|
||||||
|
let model = ActixAdminModel::create_from_payload(payload).await.unwrap();
|
||||||
|
|
||||||
if model.has_errors() {
|
if model.has_errors() {
|
||||||
let mut ctx = Context::new();
|
let mut ctx = Context::new();
|
||||||
ctx.insert("entity_names", &entity_names);
|
ctx.insert("entity_names", &actix_admin.entity_names);
|
||||||
ctx.insert("view_model", &view_model);
|
ctx.insert("view_model", &view_model);
|
||||||
ctx.insert("select_lists", &E::get_select_lists(db).await);
|
ctx.insert("select_lists", &E::get_select_lists(db).await);
|
||||||
ctx.insert("list_link", &E::get_list_link(&entity_name));
|
ctx.insert("list_link", &E::get_list_link(&entity_name));
|
||||||
@ -56,6 +51,11 @@ async fn create_or_edit_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTr
|
|||||||
Ok(HttpResponse::Ok().content_type("text/html").body(body))
|
Ok(HttpResponse::Ok().content_type("text/html").body(body))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
match id {
|
||||||
|
Some(id) => E::edit_entity(db, id, model).await,
|
||||||
|
None => E::create_entity(db, model).await
|
||||||
|
};
|
||||||
|
|
||||||
Ok(HttpResponse::SeeOther()
|
Ok(HttpResponse::SeeOther()
|
||||||
.append_header((
|
.append_header((
|
||||||
header::LOCATION,
|
header::LOCATION,
|
||||||
|
@ -2,27 +2,43 @@ use actix_web::{web, Error, HttpRequest, HttpResponse};
|
|||||||
use actix_web::http::header;
|
use actix_web::http::header;
|
||||||
use actix_session::{Session};
|
use actix_session::{Session};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use tera::{Context};
|
||||||
|
use super::{ user_can_access_page, render_unauthorized};
|
||||||
|
|
||||||
pub async fn delete<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
|
pub async fn delete<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait + ActixAdminViewModelAccessTrait>(
|
||||||
|
session: Session,
|
||||||
_req: HttpRequest,
|
_req: HttpRequest,
|
||||||
data: web::Data<T>,
|
data: web::Data<T>,
|
||||||
_text: String,
|
_text: String,
|
||||||
id: web::Path<i32>
|
id: web::Path<i32>
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let db = &data.get_db();
|
let actix_admin = data.get_actix_admin();
|
||||||
|
if !user_can_access_page::<E>(&session, actix_admin) {
|
||||||
|
let mut ctx = Context::new();
|
||||||
|
ctx.insert("render_partial", &true);
|
||||||
|
return render_unauthorized(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
let db = &data.get_db();
|
||||||
let _result = E::delete_entity(db, id.into_inner()).await;
|
let _result = E::delete_entity(db, id.into_inner()).await;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok()
|
Ok(HttpResponse::Ok()
|
||||||
.finish())
|
.finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_many<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
|
pub async fn delete_many<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait + ActixAdminViewModelAccessTrait>(
|
||||||
session: Session,
|
session: Session,
|
||||||
_req: HttpRequest,
|
_req: HttpRequest,
|
||||||
data: web::Data<T>,
|
data: web::Data<T>,
|
||||||
text: String,
|
text: String,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let actix_admin = data.get_actix_admin();
|
||||||
|
if !user_can_access_page::<E>(&session, actix_admin) {
|
||||||
|
let mut ctx = Context::new();
|
||||||
|
ctx.insert("render_partial", &true);
|
||||||
|
return render_unauthorized(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
let db = &data.get_db();
|
let db = &data.get_db();
|
||||||
let entity_name = E::get_entity_name();
|
let entity_name = E::get_entity_name();
|
||||||
let entity_ids: Vec<i32> = text
|
let entity_ids: Vec<i32> = text
|
||||||
@ -31,8 +47,6 @@ pub async fn delete_many<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>
|
|||||||
.map(|id_str| id_str.replace("ids=", "").parse::<i32>().unwrap()
|
.map(|id_str| id_str.replace("ids=", "").parse::<i32>().unwrap()
|
||||||
).collect();
|
).collect();
|
||||||
|
|
||||||
// TODO: verify is user is logged in and can delete entity
|
|
||||||
|
|
||||||
// TODO: implement delete_many
|
// TODO: implement delete_many
|
||||||
for id in entity_ids {
|
for id in entity_ids {
|
||||||
let _result = E::delete_entity(db, id).await;
|
let _result = E::delete_entity(db, id).await;
|
||||||
|
@ -2,8 +2,11 @@ use actix_session::{Session};
|
|||||||
use tera::{Context};
|
use tera::{Context};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::TERA;
|
||||||
|
use actix_web::{error, Error, HttpResponse};
|
||||||
|
|
||||||
pub fn add_auth_context(session: Session, actix_admin: &ActixAdmin, ctx: &mut Context) {
|
|
||||||
|
pub fn add_auth_context(session: &Session, actix_admin: &ActixAdmin, ctx: &mut Context) {
|
||||||
let enable_auth = &actix_admin.configuration.enable_auth;
|
let enable_auth = &actix_admin.configuration.enable_auth;
|
||||||
ctx.insert("enable_auth", &enable_auth);
|
ctx.insert("enable_auth", &enable_auth);
|
||||||
if *enable_auth {
|
if *enable_auth {
|
||||||
@ -13,3 +16,23 @@ pub fn add_auth_context(session: Session, actix_admin: &ActixAdmin, ctx: &mut Co
|
|||||||
ctx.insert("logout_link", &actix_admin.configuration.logout_link);
|
ctx.insert("logout_link", &actix_admin.configuration.logout_link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn user_can_access_page<E: ActixAdminViewModelAccessTrait>(session: &Session, actix_admin: &ActixAdmin) -> bool {
|
||||||
|
let auth_is_enabled = &actix_admin.configuration.enable_auth;
|
||||||
|
let user_is_logged_in = &actix_admin.configuration.user_is_logged_in;
|
||||||
|
let user_can_access_viewmodel = E::user_can_access(session);
|
||||||
|
|
||||||
|
match (auth_is_enabled, user_can_access_viewmodel, user_is_logged_in) {
|
||||||
|
(true, true, Some(auth_func)) => auth_func(session),
|
||||||
|
(true, false, _) => false,
|
||||||
|
(true, _, None) => false,
|
||||||
|
(false, _, _) => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_unauthorized(ctx: &Context) -> Result<HttpResponse, Error> {
|
||||||
|
let body = TERA
|
||||||
|
.render("unauthorized.html", &ctx)
|
||||||
|
.map_err(|err| error::ErrorInternalServerError(err))?;
|
||||||
|
Ok(HttpResponse::Unauthorized().content_type("text/html").body(body))
|
||||||
|
}
|
@ -6,17 +6,15 @@ use crate::prelude::*;
|
|||||||
|
|
||||||
use crate::TERA;
|
use crate::TERA;
|
||||||
|
|
||||||
use super::add_auth_context;
|
use super::{ add_auth_context };
|
||||||
|
|
||||||
pub async fn index<T: ActixAdminAppDataTrait>(session: Session, data: web::Data<T>) -> Result<HttpResponse, Error> {
|
pub async fn index<T: ActixAdminAppDataTrait>(session: Session, data: web::Data<T>) -> Result<HttpResponse, Error> {
|
||||||
let entity_names = &data.get_actix_admin().entity_names;
|
|
||||||
let actix_admin = data.get_actix_admin();
|
let actix_admin = data.get_actix_admin();
|
||||||
|
|
||||||
let mut ctx = Context::new();
|
let mut ctx = Context::new();
|
||||||
ctx.insert("entity_names", &entity_names);
|
ctx.insert("entity_names", &actix_admin.entity_names);
|
||||||
|
|
||||||
add_auth_context(session, actix_admin, &mut ctx);
|
add_auth_context(&session, actix_admin, &mut ctx);
|
||||||
// TODO: show 404 if user is not logged in but auth enabled
|
|
||||||
|
|
||||||
let body = TERA
|
let body = TERA
|
||||||
.render("index.html", &ctx)
|
.render("index.html", &ctx)
|
||||||
|
@ -9,7 +9,7 @@ use crate::ActixAdminViewModel;
|
|||||||
use crate::ActixAdminModel;
|
use crate::ActixAdminModel;
|
||||||
use crate::TERA;
|
use crate::TERA;
|
||||||
use actix_session::{Session};
|
use actix_session::{Session};
|
||||||
use super::add_auth_context;
|
use super::{ add_auth_context, user_can_access_page, render_unauthorized};
|
||||||
|
|
||||||
const DEFAULT_ENTITIES_PER_PAGE: usize = 10;
|
const DEFAULT_ENTITIES_PER_PAGE: usize = 10;
|
||||||
|
|
||||||
@ -21,15 +21,23 @@ pub struct Params {
|
|||||||
search: Option<String>
|
search: Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
|
pub async fn list<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait + ActixAdminViewModelAccessTrait>(
|
||||||
session: Session,
|
session: Session,
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
data: web::Data<T>,
|
data: web::Data<T>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let entity_name = E::get_entity_name();
|
|
||||||
let actix_admin = data.get_actix_admin();
|
let actix_admin = data.get_actix_admin();
|
||||||
|
let mut ctx = Context::new();
|
||||||
|
add_auth_context(&session, actix_admin, &mut ctx);
|
||||||
|
|
||||||
|
ctx.insert("entity_names", &actix_admin.entity_names);
|
||||||
|
|
||||||
|
if !user_can_access_page::<E>(&session, actix_admin) {
|
||||||
|
return render_unauthorized(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
let entity_name = E::get_entity_name();
|
||||||
let view_model: &ActixAdminViewModel = actix_admin.view_models.get(&entity_name).unwrap();
|
let view_model: &ActixAdminViewModel = actix_admin.view_models.get(&entity_name).unwrap();
|
||||||
let entity_names = &data.get_actix_admin().entity_names;
|
|
||||||
|
|
||||||
let params = web::Query::<Params>::from_query(req.query_string()).unwrap();
|
let params = web::Query::<Params>::from_query(req.query_string()).unwrap();
|
||||||
|
|
||||||
@ -45,8 +53,6 @@ pub async fn list<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
|
|||||||
let entities = result.1;
|
let entities = result.1;
|
||||||
let num_pages = result.0;
|
let num_pages = result.0;
|
||||||
|
|
||||||
let mut ctx = Context::new();
|
|
||||||
ctx.insert("entity_names", &entity_names);
|
|
||||||
ctx.insert("entity_name", &entity_name);
|
ctx.insert("entity_name", &entity_name);
|
||||||
ctx.insert("entities", &entities);
|
ctx.insert("entities", &entities);
|
||||||
ctx.insert("page", &page);
|
ctx.insert("page", &page);
|
||||||
@ -56,8 +62,6 @@ pub async fn list<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
|
|||||||
ctx.insert("num_pages", &num_pages);
|
ctx.insert("num_pages", &num_pages);
|
||||||
ctx.insert("view_model", &view_model);
|
ctx.insert("view_model", &view_model);
|
||||||
ctx.insert("search", &search);
|
ctx.insert("search", &search);
|
||||||
// TODO: show 404 if user is not logged in but auth enabled
|
|
||||||
add_auth_context(session, actix_admin, &mut ctx);
|
|
||||||
|
|
||||||
let body = TERA
|
let body = TERA
|
||||||
.render("list.html", &ctx)
|
.render("list.html", &ctx)
|
||||||
|
@ -14,4 +14,4 @@ mod delete;
|
|||||||
pub use delete::{ delete, delete_many };
|
pub use delete::{ delete, delete_many };
|
||||||
|
|
||||||
mod helpers;
|
mod helpers;
|
||||||
pub use helpers::{ add_auth_context };
|
pub use helpers::{ add_auth_context, user_can_access_page, render_unauthorized };
|
@ -3,6 +3,7 @@ use sea_orm::DatabaseConnection;
|
|||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::ActixAdminModel;
|
use crate::ActixAdminModel;
|
||||||
|
use actix_session::{Session};
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
pub trait ActixAdminViewModelTrait {
|
pub trait ActixAdminViewModelTrait {
|
||||||
@ -27,6 +28,10 @@ pub trait ActixAdminViewModelTrait {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ActixAdminViewModelAccessTrait {
|
||||||
|
fn user_can_access(session: &Session) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
pub struct ActixAdminViewModel {
|
pub struct ActixAdminViewModel {
|
||||||
pub entity_name: String,
|
pub entity_name: String,
|
||||||
|
5
templates/unauthorized.html
Normal file
5
templates/unauthorized.html
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
Unauthorized
|
||||||
|
{% endblock content %}
|
Loading…
Reference in New Issue
Block a user