chore: add and fix hanldlers to use openid with casdoor for Single Sign-On

This commit is contained in:
Jesús Pérez 2023-09-17 01:34:23 +01:00
parent 4cbdf902f8
commit 97a411bdc8

View File

@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::sync::Arc;
use urlencoding::{encode,decode};
// use tokio::sync::RwLock;
@ -33,6 +34,9 @@ use crate::{
TotpMode,
Random,
AppConnectInfo,
OpenidData,
OpenidCli,
// OpenidClaims,
// UserNotifyData,
// TOKEN_AUTH_VALUE,
// TOKEN_KEY_VALUE,
@ -73,7 +77,7 @@ pub fn users_router_handlers() -> Router {
// }
let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
// has_cookie=true;
// TODO check value
// TODO check value
// let sid: String = format!("{}",&auth_state.sid());
// println!("auth_sid: {}",&sid);
// println!("id: {}",&auth_state.id());
@ -205,7 +209,7 @@ pub fn users_router_handlers() -> Router {
"signup_handler"
);
if !auth_state.is_admin() {
let total_users = User::count(&app_dbs.user_store).await.unwrap_or_else(|e|{
let total_users = User::count(&app_dbs.user_store).await.unwrap_or_else(|e|{
println!("Count error: {}",e);
0
});
@ -215,7 +219,7 @@ pub fn users_router_handlers() -> Router {
let _ = req_handler.trace_req(msg);
return Redirect::temporary( &format!("/")).into_response();
}
let total_users = User::count(&app_dbs.user_store).await.unwrap_or_else(|e|{
let total_users = User::count(&app_dbs.user_store).await.unwrap_or_else(|e|{
println!("Count error: {}",e);
-1
});
@ -237,7 +241,7 @@ pub fn users_router_handlers() -> Router {
Ok(totp) => {
req_handler.context.insert("otp_code", &totp.get_secret_base32());
req_handler.context.insert("otp_url", &totp.get_url());
req_handler.context.insert("otp_qr", &totp.get_qr().unwrap_or_default());
req_handler.context.insert("otp_qr", &totp.get_qr_base64().unwrap_or_default());
req_handler.context.insert("totp_digits", &app_dbs.config.totp_digits);
req_handler.context.insert("totp_algorithm",&format!("{}",&app_dbs.config.totp_algorithm));
},
@ -347,7 +351,7 @@ pub fn users_router_handlers() -> Router {
"Error"
).into_response();
}
}
}
} else {
otp_enabled = false;
}
@ -370,14 +374,14 @@ pub fn users_router_handlers() -> Router {
} else {
DEFAULT_ROLES.to_owned()
};
let total_users = User::count(&app_dbs.user_store).await.unwrap_or_else(|e|{
let total_users = User::count(&app_dbs.user_store).await.unwrap_or_else(|e|{
let _ = req_handler.trace_req( format!("Users count error: {}",e));
println!("Count error: {}",e);
-1
});
let isadmin = if user_data.id == "A" || total_users < 1 {
true
} else {
} else {
false
};
let mut new_items = user_data.items.to_owned();
@ -428,7 +432,6 @@ pub fn users_router_handlers() -> Router {
}
};
let session_token = req_handler.new_token();
println!("session: {}", &session_token.to_string());
let session_cookie = add_session_cookie(true,&cookies, &session_token, &str_user_data, 0, &app_dbs, "/").await;
let _ = req_handler.trace_req(format!("User '{}' created",&user_data.name));
if app_dbs.config.verbose > 1 { println!("session cookie: {}", &session_cookie); }
@ -455,7 +458,7 @@ pub fn users_router_handlers() -> Router {
&random,
"login_handler"
);
let total_users = User::count(&app_dbs.user_store).await.unwrap_or_else(|e|{
let total_users = User::count(&app_dbs.user_store).await.unwrap_or_else(|e|{
let _ = req_handler.trace_req( format!("Users count error: {}",e));
println!("Count error: {}",e);
-1
@ -469,6 +472,7 @@ pub fn users_router_handlers() -> Router {
if req_handler.req_header.is_browser() {
res_headers.append(axum::http::header::CONTENT_TYPE,"text/html; charset=utf-8".parse().unwrap());
}
req_handler.context.insert("openid_auths", &app_dbs.config.openid_auths.clone().into_keys().collect::<Vec<String>>());
// req_handler.context.insert("with_menu", "1");
req_handler.context.insert("use_mail", &app_dbs.config.use_mail);
req_handler.context.insert("totp_mode", &format!("{}",&app_dbs.config.totp_mode));
@ -487,14 +491,90 @@ pub fn users_router_handlers() -> Router {
result.to_owned()
).into_response()
}
async fn post_login_handler(
async fn login_openid_handler(
header: HeaderMap,
uri: Uri,
Extension(app_dbs): Extension<Arc<AppDBs>>,
Extension(cookies): Extension<Cookies>,
Extension(random): Extension<Random>,
Extension(openid_clients): Extension<Arc<HashMap<String,OpenidCli>>>,
ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
Json(user_login): Json<UserLogin>,
axum::extract::Path(data): axum::extract::Path<String>,
) -> Response {
dbg!(&header);
dbg!(&uri);
dbg!(&data);
SessionStoreDB::cleanup_data(&app_dbs).await;
let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
let req_handler = ReqHandler::new(
ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
&app_dbs,
&uri,
&auth_state,
&random,
"login_handler"
);
let total_users = User::count(&app_dbs.user_store).await.unwrap_or_else(|e|{
let _ = req_handler.trace_req( format!("Users count error: {}",e));
println!("Count error: {}",e);
-1
});
if total_users < 1 {
let _ = req_handler.trace_req(String::from("No users found"));
return Redirect::temporary( &format!("/signup")).into_response();
}
if data.is_empty() {
return Redirect::temporary( &format!("/login")).into_response();
}
let (str_user_data,url) = if let Some(openid_conf) = app_dbs.config.openid_auths.get(&data) {
if let Some(openid_cli) = openid_clients.get(&data) {
dbg!(&openid_conf);
match openid_conf.get_auth(openid_cli).await {
Ok(openid) => {
//let _ = req_handler.trace_req(format!("User '{}' created",&user_data.name));
let str_user_data = format!("{}|{}|{}", &data, openid.token.secret(), openid.nonce.secret());
//let session_cookie = add_session_cookie(true,&cookies, &session_token, &str_user_data, 0, &app_dbs, "/").await;
//if app_dbs.config.verbose > 1 { println!("session cookie: {}", &session_cookie) };
//let _new_auth_state = AuthState::from_cookie(session_cookie.to_string(), &app_dbs).await;
/*req_handler = ReqHandler::new(
req_handler.req_header,
&app_dbs,
&uri,
&new_auth_state,
&random,
"post_login_handler"
);*/
//let _ = req_handler.trace_req(format!("user '{}', new token: '{}', cookie: '{}' ",&user_login.name, &session_token, &session_cookie));
(str_user_data, format!("{}", openid.url.to_string()))
},
Err(e) => {
println!("Error openid login handler: {}", e);
(String::from(""), String::from("/login"))
}
}
} else {
(String::from(""), String::from("/login"))
}
} else {
(String::from(""), String::from("/login"))
};
let session_token = req_handler.new_token();
dbg!(&url);
let session_cookie = add_session_cookie(true,&cookies, &session_token, &str_user_data, 0, &app_dbs, "/").await;
if app_dbs.config.verbose > 1 { println!("session cookie: {}", &session_cookie) };
let _new_auth_state = AuthState::from_cookie(session_cookie.to_string(), &app_dbs).await;
return Redirect::temporary(&format!("{}",url)).into_response()
}
async fn login_user (
header: HeaderMap,
uri: Uri,
app_dbs: Arc<AppDBs>,
cookies: Cookies,
random: Random,
app_connect_info: AppConnectInfo,
user_login: UserLogin,
source: &str,
) -> Response {
let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
let mut req_handler = ReqHandler::new(
@ -505,7 +585,8 @@ pub fn users_router_handlers() -> Router {
&random,
"post_login_handler"
);
if user_login.name.is_empty() || user_login.password.is_empty() {
dbg!(&user_login);
if user_login.name.is_empty() || source != "from_login_openid" && user_login.password.is_empty() {
let _ = req_handler.trace_req(String::from("Empty name or password"));
// return Err(error_page(&SignupError::MissingDetails));
return (
@ -514,6 +595,8 @@ pub fn users_router_handlers() -> Router {
"Error"
).into_response();
}
dbg!(&user_login);
dbg!(&source);
let field = if user_login.name.contains("@") {
"email"
} else {
@ -523,6 +606,7 @@ pub fn users_router_handlers() -> Router {
println!("Error select: {}", e);
User::default()
});
dbg!(&user_sel);
if user_sel.name.is_empty() {
let _ = req_handler.trace_req(format!("No name '{}' found",&user_login.name));
// User not exists
@ -532,6 +616,7 @@ pub fn users_router_handlers() -> Router {
"Error data"
).into_response();
}
dbg!(&user_sel);
if user_sel.status != UserStatus::Active && user_sel.status != UserStatus::Created {
let _ = req_handler.trace_req(format!("user '{}' in not valid status: {}",&user_login.name, &user_sel.status));
return (
@ -540,53 +625,59 @@ pub fn users_router_handlers() -> Router {
"Error status"
).into_response();
}
if verify_password(&user_login.password, &user_sel.password).is_err() {
let _ = req_handler.trace_req(format!("user '{}' not valid password: {}",&user_login.name, &user_sel.password));
println!("password NOT valid");
// TODO
//return Err(error_page(&SignupError::PasswordsDoNotMatch))
return (
StatusCode::BAD_REQUEST,
req_handler.req_header.header,
"Error data"
).into_response();
}
let result=format!("{}:{}","OK",&user_sel.otp_verified);
if app_dbs.config.totp_mode != TotpMode::No {
if user_login.otp_auth.is_empty()
&& (app_dbs.config.totp_mode == TotpMode::Mandatory || user_sel.otp_enabled)
{
let _ = req_handler.trace_req(format!("user '{}' not valid Totp: {}",&user_login.name, &user_sel.otp_enabled));
dbg!(&user_sel);
let result = if source == "from_login_openid" {
format!("{}:true", "OK")
} else {
if verify_password(&user_login.password, &user_sel.password).is_err() {
let _ = req_handler.trace_req(format!("user '{}' not valid password: {}", &user_login.name, &user_sel.password));
println!("password NOT valid");
// TODO
//return Err(error_page(&SignupError::PasswordsDoNotMatch))
return (
StatusCode::BAD_REQUEST,
req_handler.req_header.header,
result
"Error data"
).into_response();
} else if user_sel.otp_enabled && user_sel.otp_verified
&& !user_sel.otp_base32.is_empty() && !user_sel.otp_defs.is_empty()
{
match req_handler.otp_check(&user_sel.otp_base32,&user_login.otp_auth, &user_sel.otp_defs) {
Ok(val) => {
if !val {
let _ = req_handler.trace_req(format!("user '{}' not valid TOTP code",&user_login.name));
}
let result = format!("{}:{}", "OK", &user_sel.otp_verified);
if app_dbs.config.totp_mode != TotpMode::No {
if user_login.otp_auth.is_empty()
&& (app_dbs.config.totp_mode == TotpMode::Mandatory || user_sel.otp_enabled)
{
let _ = req_handler.trace_req(format!("user '{}' not valid Totp: {}", &user_login.name, &user_sel.otp_enabled));
return (
req_handler.req_header.header,
result
).into_response();
} else if user_sel.otp_enabled && user_sel.otp_verified
&& !user_sel.otp_base32.is_empty() && !user_sel.otp_defs.is_empty()
{
match req_handler.otp_check(&user_sel.otp_base32, &user_login.otp_auth, &user_sel.otp_defs) {
Ok(val) => {
if !val {
let _ = req_handler.trace_req(format!("user '{}' not valid TOTP code", &user_login.name));
return (
StatusCode::UNAUTHORIZED,
req_handler.req_header.header,
"Error"
).into_response();
}
},
Err(e) => {
let _ = req_handler.trace_req(format!("user '{}' TOTP check error: {}", &user_login.name, e));
println!("TOTP check: {}", e);
return (
StatusCode::UNAUTHORIZED,
req_handler.req_header.header,
"Error"
).into_response();
}
},
Err(e) => {
let _ = req_handler.trace_req(format!("user '{}' TOTP check error: {}",&user_login.name,e));
println!("TOTP check: {}", e);
return (
StatusCode::UNAUTHORIZED,
req_handler.req_header.header,
"Error"
).into_response();
}
}
}
}
}
}
result
};
user_sel.lastaccess = chrono::Utc::now().timestamp().to_string();
if user_sel.status != UserStatus::Active { user_sel.status = UserStatus::Active }
let user_data = user_sel.session_data();
@ -610,11 +701,11 @@ pub fn users_router_handlers() -> Router {
if app_dbs.config.protocol == "https" {
(
req_handler.req_header.header,
result
result
).into_response()
} else {
(
result
result
).into_response()
}
},
@ -628,7 +719,78 @@ pub fn users_router_handlers() -> Router {
}
}
}
async fn post_login_handler(
header: HeaderMap,
uri: Uri,
Extension(app_dbs): Extension<Arc<AppDBs>>,
Extension(cookies): Extension<Cookies>,
Extension(random): Extension<Random>,
ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
Json(user_login): Json<UserLogin>,
) -> Response {
login_user ( header, uri,
app_dbs, cookies, random,
app_connect_info,
user_login,
"post_login"
).await
}
async fn from_login_openid_handler(
header: HeaderMap,
uri: Uri,
Extension(app_dbs): Extension<Arc<AppDBs>>,
Extension(cookies): Extension<Cookies>,
Extension(random): Extension<Random>,
ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
Extension(openid_clients): Extension<Arc<HashMap<String,OpenidCli>>>,
//axum::extract::Path(data): axum::extract::Path<String>,
data: axum::extract::Query<OpenidData>,
// Json(user_login): Json<UserLogin>,
) -> Response {
let auth_state = get_auth_state(true, &cookies, &app_dbs).await;
let user_data = auth_state.user_data();
//let _req_handler = ReqHandler::new(
// ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info),
// &app_dbs,
// &uri,
// &auth_state,
// &random,
// "post_login_handler"
//);
if user_data.len() > 1 && user_data[1] == data.state {
println!("OK State {} == {}", user_data[1], data.state);
}
let mut user_login = UserLogin::default();
if let Some(openid_conf) = app_dbs.config.openid_auths.get(&user_data[0]) {
if let Some(openid_cli) = openid_clients.get(&user_data[0]) {
dbg!(openid_conf);
match openid_conf.get_token(openid_cli, data.code.to_owned(), user_data[2].to_owned()).await {
Ok(data) => {
dbg!("{}", &data);
user_login = UserLogin {
name: data.name.to_owned(),
password: String::from(""),
otp_auth: String::from(""),
email: data.email.to_owned(),
};
},
Err(e) => {
println!("Error openid login handler: {}", e);
}
}
}
}
if ! user_login.name.is_empty() {
let _res = login_user(header, uri,
app_dbs, cookies, random,
app_connect_info,
user_login,
"from_login_openid"
).await;
}
let url="https://tiicl.tls13.io:8800";
Redirect::temporary(&format!("{}",url)).into_response()
}
async fn logout_handler(
header: HeaderMap,
uri: Uri,
@ -856,7 +1018,7 @@ pub fn users_router_handlers() -> Router {
Ok(totp) => {
req_handler.context.insert("otp_code", &user_sel.otp_base32);
req_handler.context.insert("otp_url", &user_sel.otp_auth_url);
req_handler.context.insert("otp_qr", &totp.get_qr().unwrap_or_default());
req_handler.context.insert("otp_qr", &totp.get_qr_base64().unwrap_or_default());
},
Err(e) => {
println!("User settings error totp: {}",e);
@ -867,7 +1029,7 @@ pub fn users_router_handlers() -> Router {
Ok(totp) => {
req_handler.context.insert("otp_code", &totp.get_secret_base32());
req_handler.context.insert("otp_url", &totp.get_url());
req_handler.context.insert("otp_qr", &totp.get_qr().unwrap_or_default());
req_handler.context.insert("otp_qr", &totp.get_qr_base64().unwrap_or_default());
},
Err(e) => {
println!("Error TOTP generartor: {}",e);
@ -1707,6 +1869,10 @@ pub fn users_router_handlers() -> Router {
.route("/login", get(login_handler))
.route("/login", post(post_login_handler))
.route("/openid/:data", get(login_openid_handler))
//.route("/from_openid/:data", get(from_login_openid_handler))
.route("/from_openid", get(from_login_openid_handler))
.route("/signup", get(signup_handler))
.route("/signup", post(post_signup_handler))