Make auth-info endpoint machine readable
This commit is contained in:
parent
e2f5b04ff1
commit
506791fa6a
7 changed files with 112 additions and 36 deletions
20
.sqlx/query-88c56af867c22de3d86839e86a2f1d7b75f2527d59ee4ea594c0cab0018bcd48.json
generated
Normal file
20
.sqlx/query-88c56af867c22de3d86839e86a2f1d7b75f2527d59ee4ea594c0cab0018bcd48.json
generated
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT privilege.name FROM user \n INNER JOIN user_role ON user.name = user_role.user_name \n INNER JOIN role ON user_role.role_name = role.name \n INNER JOIN role_privilege ON role.name = role_privilege.role_name \n INNER JOIN privilege ON role_privilege.privilege_name = privilege.name \n WHERE user.name = ?",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "88c56af867c22de3d86839e86a2f1d7b75f2527d59ee4ea594c0cab0018bcd48"
|
||||
}
|
||||
|
|
@ -33,17 +33,22 @@ type BookingService = service_impl::booking::BookingServiceImpl<
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct RestStateImpl {
|
||||
user_service: Arc<UserService>,
|
||||
permission_service: Arc<PermissionService>,
|
||||
slot_service: Arc<SlotService>,
|
||||
sales_person_service: Arc<SalesPersonService>,
|
||||
booking_service: Arc<BookingService>,
|
||||
}
|
||||
impl rest::RestStateDef for RestStateImpl {
|
||||
type UserService = UserService;
|
||||
type PermissionService = PermissionService;
|
||||
type SlotService = SlotService;
|
||||
type SalesPersonService = SalesPersonService;
|
||||
type BookingService = BookingService;
|
||||
|
||||
fn user_service(&self) -> Arc<Self::UserService> {
|
||||
self.user_service.clone()
|
||||
}
|
||||
fn permission_service(&self) -> Arc<Self::PermissionService> {
|
||||
self.permission_service.clone()
|
||||
}
|
||||
|
|
@ -74,9 +79,10 @@ impl RestStateImpl {
|
|||
let user_service = service_impl::UserServiceDev;
|
||||
#[cfg(feature = "oidc")]
|
||||
let user_service = service_impl::UserServiceImpl;
|
||||
let user_service = Arc::new(user_service);
|
||||
let permission_service = Arc::new(service_impl::PermissionServiceImpl::new(
|
||||
permission_dao.into(),
|
||||
user_service.into(),
|
||||
user_service.clone(),
|
||||
));
|
||||
let clock_service = Arc::new(service_impl::clock::ClockServiceImpl);
|
||||
let uuid_service = Arc::new(service_impl::uuid_service::UuidServiceImpl);
|
||||
|
|
@ -102,6 +108,7 @@ impl RestStateImpl {
|
|||
slot_service.clone(),
|
||||
));
|
||||
Self {
|
||||
user_service,
|
||||
permission_service,
|
||||
slot_service,
|
||||
sales_person_service,
|
||||
|
|
|
|||
|
|
@ -50,4 +50,6 @@ pub trait PermissionDao {
|
|||
) -> Result<(), DaoError>;
|
||||
async fn delete_role_privilege(&self, role: &str, privilege: &str) -> Result<(), DaoError>;
|
||||
async fn delete_user_role(&self, user: &str, role: &str) -> Result<(), DaoError>;
|
||||
|
||||
async fn privileges_for_user(&self, user: &str) -> Result<Arc<[PrivilegeEntity]>, DaoError>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use dao::DaoError;
|
||||
use dao::{DaoError, PrivilegeEntity};
|
||||
use sqlx::{query, query_as, SqlitePool};
|
||||
|
||||
pub mod booking;
|
||||
|
|
@ -211,4 +211,21 @@ impl dao::PermissionDao for PermissionDaoImpl {
|
|||
.map_db_error()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn privileges_for_user(&self, user: &str) -> Result<Arc<[PrivilegeEntity]>, DaoError> {
|
||||
Ok(
|
||||
query_as!(PrivilegeEntity, r"SELECT privilege.name FROM user
|
||||
INNER JOIN user_role ON user.name = user_role.user_name
|
||||
INNER JOIN role ON user_role.role_name = role.name
|
||||
INNER JOIN role_privilege ON role.name = role_privilege.role_name
|
||||
INNER JOIN privilege ON role_privilege.privilege_name = privilege.name
|
||||
WHERE user.name = ?",
|
||||
user
|
||||
)
|
||||
.fetch_all(self.pool.as_ref())
|
||||
.await
|
||||
.map(Arc::<[PrivilegeEntity]>::from)
|
||||
.map_db_error()?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,14 +5,18 @@ mod permission;
|
|||
mod sales_person;
|
||||
mod slot;
|
||||
|
||||
use axum::extract::Request;
|
||||
use axum::extract::{Request, State};
|
||||
use axum::http::Uri;
|
||||
use axum::middleware::{self, Next};
|
||||
use axum::response::{IntoResponse, Redirect};
|
||||
use axum::routing::get;
|
||||
use axum::Extension;
|
||||
use axum::{body::Body, error_handling::HandleErrorLayer, response::Response, Router};
|
||||
#[cfg(feature = "oidc")]
|
||||
use axum_oidc::{EmptyAdditionalClaims, OidcClaims};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use service::user_service::UserService;
|
||||
use service::PermissionService;
|
||||
use service::ServiceError;
|
||||
use thiserror::Error;
|
||||
use time::Duration;
|
||||
|
|
@ -177,6 +181,7 @@ fn error_handler(result: Result<Response, RestError>) -> Response {
|
|||
}
|
||||
|
||||
pub trait RestStateDef: Clone + Send + Sync + 'static {
|
||||
type UserService: service::user_service::UserService<Context = Context> + Send + Sync + 'static;
|
||||
type PermissionService: service::PermissionService<Context = Context> + Send + Sync + 'static;
|
||||
type SlotService: service::slot::SlotService<Context = Context> + Send + Sync + 'static;
|
||||
type SalesPersonService: service::sales_person::SalesPersonService<Context = Context>
|
||||
|
|
@ -185,6 +190,7 @@ pub trait RestStateDef: Clone + Send + Sync + 'static {
|
|||
+ 'static;
|
||||
type BookingService: service::booking::BookingService<Context = Context> + Send + Sync + 'static;
|
||||
|
||||
fn user_service(&self) -> Arc<Self::UserService>;
|
||||
fn permission_service(&self) -> Arc<Self::PermissionService>;
|
||||
fn slot_service(&self) -> Arc<Self::SlotService>;
|
||||
fn sales_person_service(&self) -> Arc<Self::SalesPersonService>;
|
||||
|
|
@ -220,37 +226,36 @@ pub async fn login() -> Redirect {
|
|||
Redirect::to("/")
|
||||
}
|
||||
|
||||
#[cfg(feature = "oidc")]
|
||||
pub async fn auth_info(claims: Option<OidcClaims<EmptyAdditionalClaims>>) -> Response {
|
||||
if let Some(oidc_claims) = claims {
|
||||
let username = oidc_claims
|
||||
.preferred_username()
|
||||
.map(|s| s.as_str().to_string())
|
||||
.unwrap_or_else(|| "NoUsername".to_string());
|
||||
let email = oidc_claims
|
||||
.email()
|
||||
.map(|s| s.as_str().to_string())
|
||||
.unwrap_or_else(|| "MailNotSet".to_string());
|
||||
let name = oidc_claims
|
||||
.name()
|
||||
.map(|s| {
|
||||
s.iter()
|
||||
.next()
|
||||
.map(|s| s.1.as_str().to_string())
|
||||
.unwrap_or_else(|| "NoLocalizedName".to_string())
|
||||
})
|
||||
.unwrap_or_else(|| "NameNotSet".to_string());
|
||||
let body = format!(
|
||||
"Hello, {}! Your email is {} and your username is {}",
|
||||
name, email, username
|
||||
);
|
||||
Response::builder()
|
||||
.status(200)
|
||||
.body(Body::new(body))
|
||||
.unwrap()
|
||||
} else {
|
||||
Response::builder().status(401).body(Body::empty()).unwrap()
|
||||
}
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct AuthInfoTO {
|
||||
pub user: Arc<str>,
|
||||
pub privileges: Arc<[Arc<str>]>,
|
||||
}
|
||||
|
||||
pub async fn auth_info<RestState: RestStateDef>(
|
||||
rest_state: State<RestState>,
|
||||
Extension(context): Extension<Context>,
|
||||
) -> Response {
|
||||
let user = rest_state
|
||||
.user_service()
|
||||
.current_user(context.clone().into())
|
||||
.await
|
||||
.unwrap_or_else(|_| "NoUser".into());
|
||||
let privileges: Arc<[Arc<str>]> = rest_state
|
||||
.permission_service()
|
||||
.get_privileges_for_current_user(context.into())
|
||||
.await
|
||||
.unwrap_or_else(|_| Arc::new([]))
|
||||
.into_iter()
|
||||
.map(|privilege| privilege.name.clone())
|
||||
.collect();
|
||||
let auth_info = AuthInfoTO { user, privileges };
|
||||
|
||||
let response = serde_json::to_string(&AuthInfoTO::from(auth_info)).unwrap();
|
||||
Response::builder()
|
||||
.status(200)
|
||||
.body(Body::new(response))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn start_server<RestState: RestStateDef>(rest_state: RestState) {
|
||||
|
|
@ -269,10 +274,10 @@ pub async fn start_server<RestState: RestStateDef>(rest_state: RestState) {
|
|||
|
||||
app.route("/authenticate", get(login))
|
||||
.layer(oidc_login_service)
|
||||
.route("/auth-info", get(auth_info))
|
||||
};
|
||||
|
||||
let app = app
|
||||
.route("/auth-info", get(auth_info::<RestState>))
|
||||
.nest("/permission", permission::generate_route())
|
||||
.nest("/slot", slot::generate_route())
|
||||
.nest("/sales-person", sales_person::generate_route())
|
||||
|
|
|
|||
|
|
@ -65,6 +65,10 @@ pub trait PermissionService {
|
|||
privilege: &str,
|
||||
context: Authentication<Self::Context>,
|
||||
) -> Result<(), ServiceError>;
|
||||
async fn get_privileges_for_current_user(
|
||||
&self,
|
||||
context: Authentication<Self::Context>,
|
||||
) -> Result<Arc<[Privilege]>, ServiceError>;
|
||||
|
||||
async fn create_user(
|
||||
&self,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||
|
||||
use async_trait::async_trait;
|
||||
use service::permission::Authentication;
|
||||
use service::ServiceError;
|
||||
use service::{Privilege, ServiceError};
|
||||
|
||||
pub struct PermissionServiceImpl<PermissionDao, UserService>
|
||||
where
|
||||
|
|
@ -58,6 +58,27 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
async fn get_privileges_for_current_user(
|
||||
&self,
|
||||
context: Authentication<Self::Context>,
|
||||
) -> Result<Arc<[Privilege]>, ServiceError> {
|
||||
match context {
|
||||
Authentication::Full => Ok(Arc::new([Privilege {
|
||||
name: "god-mode".into(),
|
||||
}])),
|
||||
Authentication::Context(context) => {
|
||||
let current_user = self.user_service.current_user(context).await?;
|
||||
Ok(self
|
||||
.permission_dao
|
||||
.privileges_for_user(current_user.as_ref())
|
||||
.await?
|
||||
.iter()
|
||||
.map(service::Privilege::from)
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_user(
|
||||
&self,
|
||||
user: &str,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue