Compare commits
2 Commits
eab9bdf7f5
...
372dfa3f31
Author | SHA1 | Date | |
---|---|---|---|
372dfa3f31 | |||
e07dee7fde |
8
src/controllers/base_controller.rs
Normal file
8
src/controllers/base_controller.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use actix_web::HttpResponse;
|
||||||
|
|
||||||
|
/// Create a redirect user response
|
||||||
|
pub fn redirect_user(uri: &str) -> HttpResponse {
|
||||||
|
HttpResponse::Found()
|
||||||
|
.append_header(("Location", uri))
|
||||||
|
.finish()
|
||||||
|
}
|
@ -6,6 +6,8 @@ use askama::Template;
|
|||||||
use crate::actors::users_actor::{LoginResult, UsersActor};
|
use crate::actors::users_actor::{LoginResult, UsersActor};
|
||||||
use crate::actors::users_actor;
|
use crate::actors::users_actor;
|
||||||
use crate::constants::APP_NAME;
|
use crate::constants::APP_NAME;
|
||||||
|
use crate::controllers::base_controller::redirect_user;
|
||||||
|
use crate::data::session_identity::SessionIdentity;
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "base_login_page.html")]
|
#[template(path = "base_login_page.html")]
|
||||||
@ -24,28 +26,59 @@ struct LoginTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct LoginRequest {
|
pub struct LoginRequestBody {
|
||||||
login: String,
|
login: String,
|
||||||
password: String,
|
password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct LoginRequestQuery {
|
||||||
|
logout: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Authenticate user
|
/// Authenticate user
|
||||||
pub async fn login_route(users: web::Data<Addr<UsersActor>>,
|
pub async fn login_route(users: web::Data<Addr<UsersActor>>,
|
||||||
req: Option<web::Form<LoginRequest>>,
|
query: web::Query<LoginRequestQuery>,
|
||||||
|
req: Option<web::Form<LoginRequestBody>>,
|
||||||
id: Identity) -> impl Responder {
|
id: Identity) -> impl Responder {
|
||||||
let mut danger = String::new();
|
let mut danger = String::new();
|
||||||
|
let mut success = String::new();
|
||||||
let mut login = String::new();
|
let mut login = String::new();
|
||||||
|
|
||||||
|
// Check if user session must be closed
|
||||||
|
if let Some(true) = query.logout {
|
||||||
|
id.forget();
|
||||||
|
success = "Goodbye!".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user is already authenticated
|
||||||
|
if SessionIdentity::is_authenticated(&id) {
|
||||||
|
return redirect_user("/");
|
||||||
|
}
|
||||||
|
|
||||||
// Try to authenticate user
|
// Try to authenticate user
|
||||||
if let Some(req) = &req {
|
if let Some(req) = &req {
|
||||||
|
// TODO : check request origin
|
||||||
|
|
||||||
login = req.login.clone();
|
login = req.login.clone();
|
||||||
let response: LoginResult = users.send(users_actor::LoginRequest {
|
let response: LoginResult = users.send(users_actor::LoginRequest {
|
||||||
login: login.clone(),
|
login: login.clone(),
|
||||||
password: req.password.clone(),
|
password: req.password.clone(),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
|
|
||||||
// TODO : save auth in case of successful authentication
|
match response {
|
||||||
danger = format!("{:?}", response)
|
LoginResult::Success(user) => {
|
||||||
|
id.remember(SessionIdentity::from_user(&user).serialize());
|
||||||
|
|
||||||
|
return redirect_user("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
c => {
|
||||||
|
// TODO : add bruteforce detection
|
||||||
|
log::warn!("Failed login for username {} : {:?}", login, c);
|
||||||
|
danger = "Login failed.".to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -55,9 +88,14 @@ pub async fn login_route(users: web::Data<Addr<UsersActor>>,
|
|||||||
_parent: BaseLoginPage {
|
_parent: BaseLoginPage {
|
||||||
page_title: "Login",
|
page_title: "Login",
|
||||||
danger,
|
danger,
|
||||||
success: "".to_string(),
|
success,
|
||||||
app_name: APP_NAME,
|
app_name: APP_NAME,
|
||||||
},
|
},
|
||||||
login,
|
login,
|
||||||
}.render().unwrap())
|
}.render().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sign out user
|
||||||
|
pub async fn logout_route() -> impl Responder {
|
||||||
|
redirect_user("/login?logout=true")
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
pub mod assets_controller;
|
pub mod assets_controller;
|
||||||
pub mod login_controller;
|
pub mod login_controller;
|
||||||
|
pub mod base_controller;
|
@ -2,3 +2,4 @@ pub mod app_config;
|
|||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod service;
|
pub mod service;
|
||||||
pub mod entity_manager;
|
pub mod entity_manager;
|
||||||
|
pub mod session_identity;
|
40
src/data/session_identity.rs
Normal file
40
src/data/session_identity.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use actix_identity::Identity;
|
||||||
|
|
||||||
|
use crate::data::user::User;
|
||||||
|
|
||||||
|
pub struct SessionIdentity {
|
||||||
|
pub id: String,
|
||||||
|
pub is_admin: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionIdentity {
|
||||||
|
pub fn from_user(user: &User) -> Self {
|
||||||
|
Self {
|
||||||
|
id: user.uid.clone(),
|
||||||
|
is_admin: user.admin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<D: Display>(input: D) -> Self {
|
||||||
|
let input = input.to_string();
|
||||||
|
let mut iter = input.split('-');
|
||||||
|
Self {
|
||||||
|
id: iter.next().unwrap_or_default().to_string(),
|
||||||
|
is_admin: iter.next().unwrap_or_default() == "true",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize(&self) -> String {
|
||||||
|
format!("{}-{}", self.id, self.is_admin)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_authenticated(i: &Identity) -> bool {
|
||||||
|
i.identity()
|
||||||
|
.as_ref()
|
||||||
|
.map(Self::deserialize)
|
||||||
|
.map(|s| !s.id.is_empty())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ use clap::Parser;
|
|||||||
|
|
||||||
use basic_oidc::constants::{DEFAULT_ADMIN_PASSWORD, DEFAULT_ADMIN_USERNAME};
|
use basic_oidc::constants::{DEFAULT_ADMIN_PASSWORD, DEFAULT_ADMIN_USERNAME};
|
||||||
use basic_oidc::controllers::assets_controller::assets_route;
|
use basic_oidc::controllers::assets_controller::assets_route;
|
||||||
use basic_oidc::controllers::login_controller::login_route;
|
use basic_oidc::controllers::login_controller::{login_route, logout_route};
|
||||||
use basic_oidc::data::app_config::AppConfig;
|
use basic_oidc::data::app_config::AppConfig;
|
||||||
use basic_oidc::data::entity_manager::EntityManager;
|
use basic_oidc::data::entity_manager::EntityManager;
|
||||||
use basic_oidc::data::user::{hash_password, User};
|
use basic_oidc::data::user::{hash_password, User};
|
||||||
@ -81,6 +81,9 @@ async fn main() -> std::io::Result<()> {
|
|||||||
// Login page
|
// Login page
|
||||||
.route("/login", web::get().to(login_route))
|
.route("/login", web::get().to(login_route))
|
||||||
.route("/login", web::post().to(login_route))
|
.route("/login", web::post().to(login_route))
|
||||||
|
|
||||||
|
// Logout page
|
||||||
|
.route("/logout", web::get().to(logout_route))
|
||||||
})
|
})
|
||||||
.bind(config.listen_address)?
|
.bind(config.listen_address)?
|
||||||
.run()
|
.run()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user