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