feat(sso): sso login
This commit is contained in:
@@ -13,6 +13,7 @@ use crate::api::AppState;
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CallbackQuery {
|
||||
pub code: String,
|
||||
pub tenant_id: Option<String>,
|
||||
pub next: Option<String>,
|
||||
}
|
||||
|
||||
@@ -30,7 +31,6 @@ struct Code2TokenData {
|
||||
access_token: String,
|
||||
refresh_token: String,
|
||||
expires_in: usize,
|
||||
token_type: String,
|
||||
tenant_id: String,
|
||||
user_id: String,
|
||||
}
|
||||
@@ -121,6 +121,17 @@ async fn sso_callback_handler(
|
||||
return Ok(Redirect::temporary(&target).into_response());
|
||||
}
|
||||
|
||||
let tenant_id = q
|
||||
.tenant_id
|
||||
.as_deref()
|
||||
.unwrap_or("")
|
||||
.trim()
|
||||
.to_string();
|
||||
if uuid::Uuid::parse_str(&tenant_id).is_err() {
|
||||
let target = resolve_front_error_redirect("missing or invalid tenant_id");
|
||||
return Ok(Redirect::temporary(&target).into_response());
|
||||
}
|
||||
|
||||
let iam_base = std::env::var("IAM_BASE_URL")
|
||||
.or_else(|_| std::env::var("IAM_SERVICE_BASE_URL"))
|
||||
.map_err(|_| AppError::ConfigError("IAM_BASE_URL is required".into()))?;
|
||||
@@ -132,9 +143,10 @@ async fn sso_callback_handler(
|
||||
let http = reqwest::Client::new();
|
||||
let resp = http
|
||||
.post(format!(
|
||||
"{}/iam/api/v1/auth/code2token",
|
||||
"{}/api/v1/auth/code2token",
|
||||
iam_base.trim_end_matches('/')
|
||||
))
|
||||
.header("X-Tenant-ID", tenant_id)
|
||||
.json(&Code2TokenRequest {
|
||||
code: q.code,
|
||||
client_id,
|
||||
|
||||
@@ -29,7 +29,7 @@ pub fn build_router(state: AppState) -> Router {
|
||||
.nest("/media", handlers::media::router())
|
||||
.nest("/articles", handlers::article::router());
|
||||
|
||||
let app = Router::new()
|
||||
Router::new()
|
||||
.route("/favicon.ico", get(|| async { axum::http::StatusCode::NO_CONTENT }))
|
||||
.merge(Scalar::with_url("/scalar", ApiDoc::openapi()))
|
||||
.merge(health)
|
||||
@@ -37,7 +37,5 @@ pub fn build_router(state: AppState) -> Router {
|
||||
.nest("/v1", v1)
|
||||
.layer(axum::middleware::from_fn(catch_panic))
|
||||
.layer(axum::middleware::from_fn(request_logger))
|
||||
.with_state(state);
|
||||
|
||||
app
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ impl CmsServices {
|
||||
repositories::column::get_column(&self.pool, tenant_id, id).await
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn update_column(
|
||||
&self,
|
||||
tenant_id: Uuid,
|
||||
@@ -110,6 +111,7 @@ impl CmsServices {
|
||||
repositories::tag::delete_tag(&self.pool, tenant_id, id).await
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn create_media(
|
||||
&self,
|
||||
tenant_id: Uuid,
|
||||
@@ -149,6 +151,7 @@ impl CmsServices {
|
||||
repositories::media::delete_media(&self.pool, tenant_id, id).await
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn create_article(
|
||||
&self,
|
||||
tenant_id: Uuid,
|
||||
@@ -190,6 +193,7 @@ impl CmsServices {
|
||||
repositories::article::list_articles(&self.pool, tenant_id, q).await
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn update_article(
|
||||
&self,
|
||||
tenant_id: Uuid,
|
||||
|
||||
@@ -125,10 +125,10 @@ impl IamClient {
|
||||
};
|
||||
|
||||
let now = Instant::now();
|
||||
if let Some(entry) = self.inner.cache.get(&key).map(|e| e.clone()) {
|
||||
if entry.expires_at > now {
|
||||
return Ok(entry.allowed);
|
||||
}
|
||||
if let Some(entry) = self.inner.cache.get(&key).map(|e| e.clone())
|
||||
&& entry.expires_at > now
|
||||
{
|
||||
return Ok(entry.allowed);
|
||||
}
|
||||
|
||||
let remote = self
|
||||
@@ -141,17 +141,17 @@ impl IamClient {
|
||||
Ok(allowed)
|
||||
}
|
||||
Err(e) => {
|
||||
if let Some(entry) = self.inner.cache.get(&key).map(|e| e.clone()) {
|
||||
if entry.stale_until > now {
|
||||
tracing::warn!(
|
||||
tenant_id = %tenant_id,
|
||||
user_id = %user_id,
|
||||
action = "iam_client.degraded_cache",
|
||||
latency_ms = 0_u64,
|
||||
error_code = "iam_degraded_cache"
|
||||
);
|
||||
return Ok(entry.allowed);
|
||||
}
|
||||
if let Some(entry) = self.inner.cache.get(&key).map(|e| e.clone())
|
||||
&& entry.stale_until > now
|
||||
{
|
||||
tracing::warn!(
|
||||
tenant_id = %tenant_id,
|
||||
user_id = %user_id,
|
||||
action = "iam_client.degraded_cache",
|
||||
latency_ms = 0_u64,
|
||||
error_code = "iam_degraded_cache"
|
||||
);
|
||||
return Ok(entry.allowed);
|
||||
}
|
||||
Err(e)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ async fn list_tag_ids_for_article(
|
||||
Ok(tags)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn create_article(
|
||||
pool: &PgPool,
|
||||
tenant_id: Uuid,
|
||||
@@ -109,6 +110,7 @@ pub async fn get_article(
|
||||
Ok(ArticleWithTags { article, tag_ids })
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn update_article(
|
||||
pool: &PgPool,
|
||||
tenant_id: Uuid,
|
||||
|
||||
@@ -47,6 +47,7 @@ pub async fn get_column(pool: &PgPool, tenant_id: Uuid, id: Uuid) -> Result<Colu
|
||||
Ok(column)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn update_column(
|
||||
pool: &PgPool,
|
||||
tenant_id: Uuid,
|
||||
|
||||
@@ -3,6 +3,7 @@ use common_telemetry::AppError;
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn create_media(
|
||||
pool: &PgPool,
|
||||
tenant_id: Uuid,
|
||||
|
||||
Reference in New Issue
Block a user