multi-hotel-refactor #3
BIN
db/auth.sqlite
BIN
db/auth.sqlite
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -63,7 +63,7 @@ std::panic::set_hook(Box::new(|info| {
|
|||||||
//panic!("crash-test");
|
//panic!("crash-test");
|
||||||
|
|
||||||
let hotel_pools = HotelPool::new();
|
let hotel_pools = HotelPool::new();
|
||||||
let logs_manager = SqliteConnectionManager::file("db/auth.sqlite");
|
let logs_manager = SqliteConnectionManager::file("db/auth_copy_2.sqlite");
|
||||||
let logs_pool = Pool::builder()
|
let logs_pool = Pool::builder()
|
||||||
.max_size(5)
|
.max_size(5)
|
||||||
.build(logs_manager)
|
.build(logs_manager)
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ pub struct ForceUpdatePasswordValues{
|
|||||||
|
|
||||||
//pub struct ForceUpdatePasswordPayload (pub ForceUpdatePasswordValues);
|
//pub struct ForceUpdatePasswordPayload (pub ForceUpdatePasswordValues);
|
||||||
|
|
||||||
pub async fn ForceUpdatePassword(
|
pub async fn force_update_password(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Json(payload): Json<ForceUpdatePasswordValues>,
|
Json(payload): Json<ForceUpdatePasswordValues>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
@@ -191,7 +191,7 @@ pub async fn ForceUpdatePassword(
|
|||||||
|row|{
|
|row|{
|
||||||
let user_id: i32 = row.get(0)?;
|
let user_id: i32 = row.get(0)?;
|
||||||
//let hotel_id: i32 = row.get(1)?;
|
//let hotel_id: i32 = row.get(1)?;
|
||||||
Ok((user_id))
|
Ok(user_id)
|
||||||
},
|
},
|
||||||
).optional() {
|
).optional() {
|
||||||
Ok(opt) => opt,
|
Ok(opt) => opt,
|
||||||
@@ -199,7 +199,7 @@ pub async fn ForceUpdatePassword(
|
|||||||
.into_response(),
|
.into_response(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (user_id) = match user_row {
|
let user_id = match user_row {
|
||||||
Some(u) => u,
|
Some(u) => u,
|
||||||
None => return (StatusCode::UNAUTHORIZED, "Not correct user")
|
None => return (StatusCode::UNAUTHORIZED, "Not correct user")
|
||||||
.into_response(),
|
.into_response(),
|
||||||
@@ -239,7 +239,7 @@ pub struct UpdatePasswordValues{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn UpdatePassword(
|
pub async fn update_password(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Json(payload): Json<UpdatePasswordValues>,
|
Json(payload): Json<UpdatePasswordValues>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
@@ -360,7 +360,7 @@ pub async fn clean_auth_loging(
|
|||||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB query error").into_response(),
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB query error").into_response(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (user_id, stored_hash, hotel_id, displayname) = match user_row {
|
let (user_id, stored_hash, hotel_id, _displayname) = match user_row {
|
||||||
Some(u) => u,
|
Some(u) => u,
|
||||||
None => return (StatusCode::UNAUTHORIZED, "Invalid credentials").into_response(),
|
None => return (StatusCode::UNAUTHORIZED, "Invalid credentials").into_response(),
|
||||||
};
|
};
|
||||||
@@ -405,7 +405,9 @@ pub struct CreateRefreshTokenValue {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: refactor this to impl IntoResponse ans not Result
|
|
||||||
|
//FIXME: weird return type, returning result ?
|
||||||
|
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
pub async fn create_refresh_token(
|
pub async fn create_refresh_token(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
@@ -433,26 +435,76 @@ pub async fn create_refresh_token(
|
|||||||
let conn = state.logs_pool.get()
|
let conn = state.logs_pool.get()
|
||||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB connection error".to_string()))?;
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB connection error".to_string()))?;
|
||||||
|
|
||||||
let mut stmt = conn.prepare(
|
|
||||||
"SELECT id, password, hotel_id FROM users WHERE username = ?1"
|
|
||||||
).map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB prepare error".to_string()))?;
|
|
||||||
|
|
||||||
|
//TODO: prend id username password de users
|
||||||
|
// let mut stmt = conn.prepare(
|
||||||
|
// "SELECT id, password FROM users WHERE username = ?1"
|
||||||
|
|
||||||
let user_rows = stmt
|
let credentials = match conn.query_row(
|
||||||
.query_map(params![&payload.username], |row| {
|
"SELECT id, password FROM users WHERE username = ?1",
|
||||||
|
params![&payload.username],
|
||||||
|
|row| {
|
||||||
let user_id: i32 = row.get(0)?;
|
let user_id: i32 = row.get(0)?;
|
||||||
let password: String = row.get(1)?;
|
let password: String = row.get(1)?;
|
||||||
let hotel_id: i32 = row.get(2)?;
|
Ok((user_id, password))
|
||||||
Ok((user_id, password, hotel_id))
|
},
|
||||||
})
|
) {
|
||||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB query error".to_string()))?;
|
Ok(cr) => cr,
|
||||||
|
Err(_) => return Err((StatusCode::INTERNAL_SERVER_ERROR, "error fetching credentials".to_string())),
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
let (user_id, user_password) = credentials;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
let (user_id, stored_hash, hotel_id) = user_row
|
let (user_id, stored_hash, hotel_id) = user_row
|
||||||
.ok_or((StatusCode::NOT_FOUND, "User not found".to_string()))?;
|
.ok_or((StatusCode::NOT_FOUND, "User not found".to_string()))?;
|
||||||
*/
|
*/
|
||||||
//let mut tokens = Vec::new();
|
//let mut tokens = Vec::new();
|
||||||
|
//TODO: validate password
|
||||||
|
if !verify_password(&payload.password, &user_password) {
|
||||||
|
return Err((StatusCode::INTERNAL_SERVER_ERROR, "Invalid credential".to_string())); // Skip rows with invalid password
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: stmt, querry hotel-id dans hotel-user-link avec l'ID précédant
|
||||||
|
let mut stmt = match conn.prepare(
|
||||||
|
"SELECt hotel_id FROM hotel_user_link WHERE user_id = ?1"
|
||||||
|
) {
|
||||||
|
Ok(stmt) => stmt,
|
||||||
|
Err(_) => return Err((StatusCode::INTERNAL_SERVER_ERROR, "error building user_id fetch stmt".to_string())),
|
||||||
|
};
|
||||||
|
//TODO: compiler les hotel id dans un vecteur pour le feed dans le refresh token
|
||||||
|
//Deja fait ?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let hotel_ids = match stmt
|
||||||
|
.query_map(params![&user_id],|row| row.get (0))
|
||||||
|
{
|
||||||
|
Ok(rows) => rows.collect::<Result<Vec<_>,_>>()
|
||||||
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error collecting hotel_ids".to_string()))?,
|
||||||
|
Err(_) => return Err((StatusCode::INTERNAL_SERVER_ERROR, "Error mapping hotel_ids".to_string())),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO refresh_token (user_id, token_hash, device_id, user_agent, hotel_id_list)
|
||||||
|
VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||||
|
params![
|
||||||
|
&user_id,
|
||||||
|
&hashed_token,
|
||||||
|
&device_id_str,
|
||||||
|
&user_agent_str,
|
||||||
|
&hotel_ids,
|
||||||
|
],
|
||||||
|
).map_err(|e| {
|
||||||
|
(StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {}", e))
|
||||||
|
})?;
|
||||||
|
//TODO: insert single refresh token
|
||||||
|
/*
|
||||||
for user_row_result in user_rows {
|
for user_row_result in user_rows {
|
||||||
let (user_id, stored_hash, hotel_id) = user_row_result
|
let (user_id, stored_hash, hotel_id) = user_row_result
|
||||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB row error".to_string()))?;
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB row error".to_string()))?;
|
||||||
@@ -480,7 +532,8 @@ pub async fn create_refresh_token(
|
|||||||
|
|
||||||
//tokens.push(raw_token);
|
//tokens.push(raw_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
let cookie_value = format!("refresh_token={}; HttpOnly; Secure; Path=/", raw_token);
|
let cookie_value = format!("refresh_token={}; HttpOnly; Secure; Path=/", raw_token);
|
||||||
|
|
||||||
@@ -499,6 +552,7 @@ pub struct LoginRefreshTokenValues{
|
|||||||
refresh_token: String,
|
refresh_token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: LATER : implement hotel-id-selected to allow user to only get part hotels ?
|
||||||
pub async fn login_refresh_token (
|
pub async fn login_refresh_token (
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Extension(keys): Extension<JwtKeys>,
|
Extension(keys): Extension<JwtKeys>,
|
||||||
@@ -519,16 +573,19 @@ pub async fn login_refresh_token (
|
|||||||
let device_id_str = payload.device_id.to_string();
|
let device_id_str = payload.device_id.to_string();
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let user_agent_str = match user_agent {
|
let user_agent_str = match user_agent {
|
||||||
Some(ua) => ua.to_string(),
|
Some(ua) => ua.to_string(),
|
||||||
None => return (StatusCode::INTERNAL_SERVER_ERROR, "user agent unknown").into_response(),
|
None => return (StatusCode::INTERNAL_SERVER_ERROR, "user agent unknown").into_response(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let device_id_str = payload.device_id.to_string();
|
let device_id_str = payload.device_id.to_string();
|
||||||
|
|
||||||
|
|
||||||
//"SELECT user_id, token_hash, hotel_id FROM refresh_token WHERE device_id = ?1 AND user_agent = ?2",
|
//"SELECT user_id, token_hash, hotel_id FROM refresh_token WHERE device_id = ?1 AND user_agent = ?2",
|
||||||
|
|
||||||
|
//TODO: swap to query row and get hotel-id's list and not single hotel per row
|
||||||
|
//deserializing the list :
|
||||||
|
//let hotel_ids: Vec<i32> = serde_json::from_str(&stored_value)?;
|
||||||
let mut stmt = match conn.prepare(
|
let mut stmt = match conn.prepare(
|
||||||
"SELECT user_id, token_hash, hotel_id
|
"SELECT user_id, token_hash, hotel_id
|
||||||
FROM refresh_token
|
FROM refresh_token
|
||||||
@@ -549,6 +606,8 @@ let device_id_str = payload.device_id.to_string();
|
|||||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB query error").into_response(),
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB query error").into_response(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let mut entries = Vec::new();
|
let mut entries = Vec::new();
|
||||||
for r in rows {
|
for r in rows {
|
||||||
match r {
|
match r {
|
||||||
@@ -563,34 +622,35 @@ let device_id_str = payload.device_id.to_string();
|
|||||||
|
|
||||||
let mut tokens = Vec::new();
|
let mut tokens = Vec::new();
|
||||||
|
|
||||||
for (user_id, token_hash, hotel_id) in entries {
|
//FIXME: swap to "for hotel_id in entries" // interator over vector list
|
||||||
if !verify_password(&payload.refresh_token, &token_hash) {
|
for (user_id, token_hash, hotel_id) in entries {
|
||||||
// skip rows with wrong hash
|
if !verify_password(&payload.refresh_token, &token_hash) {
|
||||||
continue;
|
// skip rows with wrong hash
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//FIXME: single expiration
|
||||||
|
let expiration = match chrono::Utc::now().checked_add_signed(chrono::Duration::hours(15)) {
|
||||||
|
Some(time) => time.timestamp() as usize,
|
||||||
|
None => {
|
||||||
|
// Handle overflow — probably a 500, since this should never happen
|
||||||
|
return (StatusCode::INTERNAL_SERVER_ERROR, "Time overflow".to_string()).into_response();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let claims = serde_json::json!({
|
||||||
|
"id": user_id,
|
||||||
|
"hotel_id": hotel_id,
|
||||||
|
"exp": expiration
|
||||||
|
});
|
||||||
|
|
||||||
|
let token = match encode(&Header::default(), &claims, &keys.encoding) {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "JWT creation failed").into_response(),
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.push(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
let expiration = match chrono::Utc::now().checked_add_signed(chrono::Duration::hours(15)) {
|
|
||||||
Some(time) => time.timestamp() as usize,
|
|
||||||
None => {
|
|
||||||
// Handle overflow — probably a 500, since this should never happen
|
|
||||||
return (StatusCode::INTERNAL_SERVER_ERROR, "Time overflow".to_string()).into_response();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let claims = serde_json::json!({
|
|
||||||
"id": user_id,
|
|
||||||
"hotel_id": hotel_id,
|
|
||||||
"exp": expiration
|
|
||||||
});
|
|
||||||
|
|
||||||
let token = match encode(&Header::default(), &claims, &keys.encoding) {
|
|
||||||
Ok(t) => t,
|
|
||||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "JWT creation failed").into_response(),
|
|
||||||
};
|
|
||||||
|
|
||||||
tokens.push(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if tokens.is_empty() {
|
if tokens.is_empty() {
|
||||||
return (StatusCode::UNAUTHORIZED, "Invalid or mismatched token").into_response();
|
return (StatusCode::UNAUTHORIZED, "Invalid or mismatched token").into_response();
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ pub fn utils_routes() -> Router<AppState> {
|
|||||||
.route("/register", put(register_user))
|
.route("/register", put(register_user))
|
||||||
.route("/ws/", get(ws_handler))
|
.route("/ws/", get(ws_handler))
|
||||||
.route("/tokentest", put(token_tester))
|
.route("/tokentest", put(token_tester))
|
||||||
.route("/force_update_password", put(ForceUpdatePassword))
|
.route("/force_update_password", put(force_update_password))
|
||||||
.route("/update_password", put(UpdatePassword))
|
.route("/update_password", put(update_password))
|
||||||
|
|
||||||
.route("/create_refresh", post(create_refresh_token))
|
.route("/create_refresh", post(create_refresh_token))
|
||||||
.route("/login_refresh_token", post(login_refresh_token))
|
.route("/login_refresh_token", post(login_refresh_token))
|
||||||
|
|||||||
Reference in New Issue
Block a user