feat: replace graphql playground to altair

This commit is contained in:
2025-01-12 03:46:28 +08:00
parent 97b7bfb7fb
commit c6677d414d
30 changed files with 1262 additions and 524 deletions

View File

@@ -1,5 +1,5 @@
use async_trait::async_trait;
use axum::http::request::Parts;
use axum::http::{request::Parts, HeaderValue};
use base64::{self, Engine};
use reqwest::header::AUTHORIZATION;
@@ -76,4 +76,8 @@ impl AuthService for BasicAuthService {
}
Err(AuthError::BasicInvalidCredentials)
}
fn www_authenticate_header_value(&self) -> Option<HeaderValue> {
Some(HeaderValue::from_static(r#"Basic realm="konobangu""#))
}
}

View File

@@ -3,15 +3,16 @@ use axum::{
response::{IntoResponse, Response},
Json,
};
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AuthError {
#[error(transparent)]
OidcInitError(#[from] jwt_authorizer::error::InitError),
#[error("Invalid credentials")]
BasicInvalidCredentials,
#[error(transparent)]
OidcInitError(#[from] jwt_authorizer::error::InitError),
#[error(transparent)]
OidcJwtAuthError(#[from] jwt_authorizer::AuthError),
#[error("Extra scopes {expected} do not match found scopes {found}")]
OidcExtraScopesMatchError { expected: String, found: String },
@@ -29,8 +30,23 @@ pub enum AuthError {
OidcSubMissingError,
}
impl IntoResponse for AuthError {
fn into_response(self) -> Response {
(StatusCode::UNAUTHORIZED, Json(self.to_string())).into_response()
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AuthErrorBody {
pub error_code: i32,
pub error_msg: String,
}
impl From<AuthError> for AuthErrorBody {
fn from(value: AuthError) -> Self {
AuthErrorBody {
error_code: StatusCode::UNAUTHORIZED.as_u16() as i32,
error_msg: value.to_string(),
}
}
}
impl IntoResponse for AuthError {
fn into_response(self) -> Response {
(StatusCode::UNAUTHORIZED, Json(AuthErrorBody::from(self))).into_response()
}
}

View File

@@ -0,0 +1,63 @@
use axum::{
extract::{Request, State},
http::header,
middleware::Next,
response::{IntoResponse, Response},
};
use loco_rs::prelude::AppContext;
use crate::{app::AppContextExt, auth::AuthService};
pub async fn api_auth_middleware(
State(ctx): State<AppContext>,
request: Request,
next: Next,
) -> Response {
let auth_service = ctx.get_auth_service();
let (mut parts, body) = request.into_parts();
let mut response = match auth_service.extract_user_info(&mut parts).await {
Ok(auth_user_info) => {
let mut request = Request::from_parts(parts, body);
request.extensions_mut().insert(auth_user_info);
next.run(request).await
}
Err(auth_error) => auth_error.into_response(),
};
if let Some(header_value) = auth_service.www_authenticate_header_value() {
response
.headers_mut()
.insert(header::WWW_AUTHENTICATE, header_value);
};
response
}
pub async fn webui_auth_middleware(
State(ctx): State<AppContext>,
request: Request,
next: Next,
) -> Response {
let auth_service = ctx.get_auth_service();
let (mut parts, body) = request.into_parts();
let mut response = match auth_service.extract_user_info(&mut parts).await {
Ok(auth_user_info) => {
let mut request = Request::from_parts(parts, body);
request.extensions_mut().insert(auth_user_info);
next.run(request).await
}
Err(auth_error) => auth_error.into_response(),
};
if let Some(header_value) = auth_service.www_authenticate_header_value() {
response
.headers_mut()
.insert(header::WWW_AUTHENTICATE, header_value);
};
response
}

View File

@@ -1,9 +1,11 @@
pub mod basic;
pub mod config;
pub mod errors;
pub mod middleware;
pub mod oidc;
pub mod service;
pub use config::{AppAuthConfig, BasicAuthConfig, OidcAuthConfig};
pub use errors::AuthError;
pub use middleware::{api_auth_middleware, webui_auth_middleware};
pub use service::{AppAuthService, AuthService, AuthUserInfo};

View File

@@ -1,7 +1,7 @@
use std::collections::{HashMap, HashSet};
use async_trait::async_trait;
use axum::http::request::Parts;
use axum::http::{request::Parts, HeaderValue};
use itertools::Itertools;
use jwt_authorizer::{authorizer::Authorizer, NumericDate, OneOrArray};
use serde::{Deserialize, Serialize};
@@ -135,4 +135,8 @@ impl AuthService for OidcAuthService {
auth_type: AuthType::Oidc,
})
}
fn www_authenticate_header_value(&self) -> Option<HeaderValue> {
Some(HeaderValue::from_static(r#"Bearer realm="konobangu""#))
}
}

View File

@@ -7,6 +7,7 @@ use axum::{
use jwt_authorizer::{JwtAuthorizer, Validation};
use loco_rs::app::{AppContext, Initializer};
use once_cell::sync::OnceCell;
use reqwest::header::HeaderValue;
use super::{
basic::BasicAuthService,
@@ -16,6 +17,7 @@ use super::{
};
use crate::{app::AppContextExt as _, config::AppConfigExt, models::auth::AuthType};
#[derive(Clone, Debug)]
pub struct AuthUserInfo {
pub user_pid: String,
pub auth_type: AuthType,
@@ -40,6 +42,7 @@ impl FromRequestParts<AppContext> for AuthUserInfo {
#[async_trait]
pub trait AuthService {
async fn extract_user_info(&self, request: &mut Parts) -> Result<AuthUserInfo, AuthError>;
fn www_authenticate_header_value(&self) -> Option<HeaderValue>;
}
pub enum AppAuthService {
@@ -87,6 +90,13 @@ impl AuthService for AppAuthService {
AppAuthService::Oidc(service) => service.extract_user_info(request).await,
}
}
fn www_authenticate_header_value(&self) -> Option<HeaderValue> {
match self {
AppAuthService::Basic(service) => service.www_authenticate_header_value(),
AppAuthService::Oidc(service) => service.www_authenticate_header_value(),
}
}
}
pub struct AppAuthServiceInitializer;