forked from github-starred/komodo
* start 1.19.4 * deploy 1.19.4-dev-1 * try smaller binaries with cargo strip * deploy 1.19.4-dev-2 * smaller binaries with cargo strip * Fix Submit Dialog Button Behavior with 500 Errors on Duplicate Names (#819) * Implement enhanced error handling and messaging for resource creation * Implement improved error handling for resource creation across alerter, build, and sync * Implement error handling improvements for resource copying and validation feedback * Adjust error handling for resource creation to distinguish validation errors from unexpected system errors * Refactor resource creation error handling by removing redundant match statements and simplifying the error propagation in multiple API modules. * fmt * bump indexmap * fix account selector showing empty when account no longer found * clean up theme logic, ensure monaco and others get up to date current theme * enforce disable_non_admin_create for tags. Clean up status code responses * update server cache concurrency controller * deploy 1.19.4-dev-3 * Allow signing in by pressing enter (#830) * Improve dialog overflow handling to prevent clipping of content (#828) * Add Email notification entry to community.md (#824) * Add clickable file path to show/hide file contents in StackInfo (#827) * add clickable file path to show/hide file contents in StackInfo Also added CopyButton due to the new functionality making the file path not selectable. * Move clicking interaction to CardHeader * Avoid sync edge cases of having toggle show function capturing showContents from outside Co-authored-by: Maxwell Becker <49575486+mbecker20@users.noreply.github.com> * Format previous change * Add `default_show_contents` to `handleToggleShow` --------- Co-authored-by: Maxwell Becker <49575486+mbecker20@users.noreply.github.com> * deploy 1.19.4-dev-4 * avoid stake info ShowHideButton double toggle * Allow multiple simultaneous Action runs for use with Args * deploy 1.19.4-dev-5 * feat: persist all table sorting states including unsorted (#832) - Always save sorting state to localStorage, even when empty/unsorted - Fixes issue where 'unsorted' state was not persisted across page reloads - Ensures consistent and predictable sorting behavior for all DataTable components * autofocus on login username field (#837) * Fix unnecessary auth queries flooding console on login page (#842) * Refactor authentication error handling to use serror::Result and status codes * Enable user query only when JWT is present * Enable query execution in useRead only if JWT is present * Revert backend auth changes - keep PR focused on frontend only * Fix unnecessary API queries to unreachable servers flooding console (#843) * Implement server availability checks in various components * Refactor server availability check to ensure only healthy servers are identified * cargo fmt * fmt * Auth error handling with status codes (#841) * Refactor authentication error handling to use serror::Result and status codes * Refactor error messages * Refactor authentication error handling to include status codes and improve error messages * clean up * clean * fmt * invalid user id also UNAUTHORIZED * deploy 1.19.4-dev-6 * deploy 1.19.4-dev-7 --------- Co-authored-by: Marcel Pfennig <82059270+MP-Tool@users.noreply.github.com> Co-authored-by: jack <45038833+jackra1n@users.noreply.github.com> Co-authored-by: Guten <ywzhaifei@gmail.com> Co-authored-by: Paulo Roberto Albuquerque <paulora2405@gmail.com> Co-authored-by: Lorenzo Farnararo <2814802+baldarn@users.noreply.github.com>
164 lines
4.2 KiB
Rust
164 lines
4.2 KiB
Rust
use std::{sync::OnceLock, time::Instant};
|
|
|
|
use axum::{Router, extract::Path, http::HeaderMap, routing::post};
|
|
use derive_variants::{EnumVariants, ExtractVariant};
|
|
use komodo_client::{api::auth::*, entities::user::User};
|
|
use reqwest::StatusCode;
|
|
use resolver_api::Resolve;
|
|
use response::Response;
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json::json;
|
|
use serror::{AddStatusCode, Json};
|
|
use typeshare::typeshare;
|
|
use uuid::Uuid;
|
|
|
|
use crate::{
|
|
auth::{
|
|
get_user_id_from_headers,
|
|
github::{self, client::github_oauth_client},
|
|
google::{self, client::google_oauth_client},
|
|
oidc::{self, client::oidc_client},
|
|
},
|
|
config::core_config,
|
|
helpers::query::get_user,
|
|
state::jwt_client,
|
|
};
|
|
|
|
use super::Variant;
|
|
|
|
#[derive(Default)]
|
|
pub struct AuthArgs {
|
|
pub headers: HeaderMap,
|
|
}
|
|
|
|
#[typeshare]
|
|
#[derive(
|
|
Serialize, Deserialize, Debug, Clone, Resolve, EnumVariants,
|
|
)]
|
|
#[args(AuthArgs)]
|
|
#[response(Response)]
|
|
#[error(serror::Error)]
|
|
#[variant_derive(Debug)]
|
|
#[serde(tag = "type", content = "params")]
|
|
#[allow(clippy::enum_variant_names, clippy::large_enum_variant)]
|
|
pub enum AuthRequest {
|
|
GetLoginOptions(GetLoginOptions),
|
|
SignUpLocalUser(SignUpLocalUser),
|
|
LoginLocalUser(LoginLocalUser),
|
|
ExchangeForJwt(ExchangeForJwt),
|
|
GetUser(GetUser),
|
|
}
|
|
|
|
pub fn router() -> Router {
|
|
let mut router = Router::new()
|
|
.route("/", post(handler))
|
|
.route("/{variant}", post(variant_handler));
|
|
|
|
if core_config().local_auth {
|
|
info!("🔑 Local Login Enabled");
|
|
}
|
|
|
|
if github_oauth_client().is_some() {
|
|
info!("🔑 Github Login Enabled");
|
|
router = router.nest("/github", github::router())
|
|
}
|
|
|
|
if google_oauth_client().is_some() {
|
|
info!("🔑 Google Login Enabled");
|
|
router = router.nest("/google", google::router())
|
|
}
|
|
|
|
if core_config().oidc_enabled {
|
|
info!("🔑 OIDC Login Enabled");
|
|
router = router.nest("/oidc", oidc::router())
|
|
}
|
|
|
|
router
|
|
}
|
|
|
|
async fn variant_handler(
|
|
headers: HeaderMap,
|
|
Path(Variant { variant }): Path<Variant>,
|
|
Json(params): Json<serde_json::Value>,
|
|
) -> serror::Result<axum::response::Response> {
|
|
let req: AuthRequest = serde_json::from_value(json!({
|
|
"type": variant,
|
|
"params": params,
|
|
}))?;
|
|
handler(headers, Json(req)).await
|
|
}
|
|
|
|
#[instrument(name = "AuthHandler", level = "debug", skip(headers))]
|
|
async fn handler(
|
|
headers: HeaderMap,
|
|
Json(request): Json<AuthRequest>,
|
|
) -> serror::Result<axum::response::Response> {
|
|
let timer = Instant::now();
|
|
let req_id = Uuid::new_v4();
|
|
debug!(
|
|
"/auth request {req_id} | METHOD: {:?}",
|
|
request.extract_variant()
|
|
);
|
|
let res = request.resolve(&AuthArgs { headers }).await;
|
|
if let Err(e) = &res {
|
|
debug!("/auth request {req_id} | error: {:#}", e.error);
|
|
}
|
|
let elapsed = timer.elapsed();
|
|
debug!("/auth request {req_id} | resolve time: {elapsed:?}");
|
|
res.map(|res| res.0)
|
|
}
|
|
|
|
fn login_options_reponse() -> &'static GetLoginOptionsResponse {
|
|
static GET_LOGIN_OPTIONS_RESPONSE: OnceLock<
|
|
GetLoginOptionsResponse,
|
|
> = OnceLock::new();
|
|
GET_LOGIN_OPTIONS_RESPONSE.get_or_init(|| {
|
|
let config = core_config();
|
|
GetLoginOptionsResponse {
|
|
local: config.local_auth,
|
|
github: github_oauth_client().is_some(),
|
|
google: google_oauth_client().is_some(),
|
|
oidc: oidc_client().load().is_some(),
|
|
registration_disabled: config.disable_user_registration,
|
|
}
|
|
})
|
|
}
|
|
|
|
impl Resolve<AuthArgs> for GetLoginOptions {
|
|
#[instrument(name = "GetLoginOptions", level = "debug", skip(self))]
|
|
async fn resolve(
|
|
self,
|
|
_: &AuthArgs,
|
|
) -> serror::Result<GetLoginOptionsResponse> {
|
|
Ok(*login_options_reponse())
|
|
}
|
|
}
|
|
|
|
impl Resolve<AuthArgs> for ExchangeForJwt {
|
|
#[instrument(name = "ExchangeForJwt", level = "debug", skip(self))]
|
|
async fn resolve(
|
|
self,
|
|
_: &AuthArgs,
|
|
) -> serror::Result<ExchangeForJwtResponse> {
|
|
jwt_client()
|
|
.redeem_exchange_token(&self.token)
|
|
.await
|
|
.map_err(Into::into)
|
|
}
|
|
}
|
|
|
|
impl Resolve<AuthArgs> for GetUser {
|
|
#[instrument(name = "GetUser", level = "debug", skip(self))]
|
|
async fn resolve(
|
|
self,
|
|
AuthArgs { headers }: &AuthArgs,
|
|
) -> serror::Result<User> {
|
|
let user_id = get_user_id_from_headers(headers)
|
|
.await
|
|
.status_code(StatusCode::UNAUTHORIZED)?;
|
|
get_user(&user_id)
|
|
.await
|
|
.status_code(StatusCode::UNAUTHORIZED)
|
|
}
|
|
}
|