Prechádzať zdrojové kódy

Error messages are now shown to user + fixed: get_user_infos function was always called even if auth failed

clement 1 rok pred
rodič
commit
48fb00a045
4 zmenil súbory, kde vykonal 63 pridanie a 27 odobranie
  1. 21 3
      src/handler.rs
  2. 35 22
      src/ldap/lib.rs
  3. 5 0
      templates/formpassword.html
  4. 2 2
      templates/signin.html

+ 21 - 3
src/handler.rs

@@ -18,6 +18,9 @@ pub struct FormChangePasswd {
 }
 
 async fn get_template(template_name: String, session: Session) -> String {
+    let error_message: Option<String> = session.get("error_message")
+    .unwrap_or(None);
+
     let username: Option<String> = session.get("user_id")
     .unwrap_or(None);
     let user_is_ssh: Option<bool> = session.get("user_is_ssh")
@@ -27,6 +30,13 @@ async fn get_template(template_name: String, session: Session) -> String {
     .expect("Failed to parse template files");
 
     let mut ctx = tera::Context::new();
+    match error_message {
+        Some(error_message) => {
+            ctx.insert("error_message", &error_message);
+            session.remove("error_message");
+        },
+        None => (),
+    }
     match username {
         Some(username) => ctx.insert("username", &username),
         None => (),
@@ -80,7 +90,10 @@ pub async fn auth(ldap_wrapper: web::Data<LdapWrapper>, form: web::Form<FormLogi
             .append_header((header::LOCATION, "/home"))
             .finish();
         },
-        Err(_e) => return HttpResponse::Ok().status(StatusCode::FOUND).append_header((header::LOCATION, "/")).finish(),
+        Err(e) => {
+            session.insert("error_message", e.to_string()).unwrap();
+            return HttpResponse::Ok().status(StatusCode::FOUND).append_header((header::LOCATION, "/")).finish();
+        },
     }
 }
 
@@ -117,13 +130,15 @@ pub async fn change_password(ldap_wrapper: web::Data<LdapWrapper>, form: web::Fo
     }
 
     if form.new_password != form.new_password_conf {
+        session.insert("error_message", "Passwords do not match").unwrap();
         return HttpResponse::Ok()
         .status(StatusCode::FOUND)
         .append_header((header::LOCATION, "/changepassword"))
         .finish();
     }
 
-    if form.new_password.len() == 0 || form.new_password_conf.len() == 0 {
+    if form.new_password.len() < 12 {
+        session.insert("error_message", "Password too short (min 12 chars)").unwrap();
         return HttpResponse::Ok()
         .status(StatusCode::FOUND)
         .append_header((header::LOCATION, "/changepassword"))
@@ -139,7 +154,10 @@ pub async fn change_password(ldap_wrapper: web::Data<LdapWrapper>, form: web::Fo
             .append_header((header::LOCATION, "/home"))
             .finish();
         },
-        Err(_e) => return HttpResponse::Ok().status(StatusCode::FOUND).append_header((header::LOCATION, "/")).finish(),
+        Err(e) => {
+            session.insert("error_message", e.to_string()).unwrap();
+            return HttpResponse::Ok().status(StatusCode::FOUND).append_header((header::LOCATION, "/changepassword")).finish();
+        },
     }
 }
 

+ 35 - 22
src/ldap/lib.rs

@@ -1,19 +1,32 @@
+use core::fmt;
 use std::{collections::HashSet, vec};
 use base64::{engine::general_purpose::STANDARD, Engine as _};
 use deadpool::managed::Pool;
 use ldap3::SearchEntry;
 use regex::Regex;
 use ring::rand::{self, SecureRandom};
+use serde::Serialize;
 use sha2::{Sha512, Digest};
 
 pub mod pool;
 
 use pool::{get_ldap_pool, LdapConfig, LdapManager};
 
+# [derive(Serialize)]
 pub enum Error {
-    Authfailed(String),
-    LdapServerError(String),
+    Authfailed {message: String},
+    LdapServerError {message: String},
 }
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Error::Authfailed { message } => write!(f, "{}", message),
+            Error::LdapServerError { message } => write!(f, "LDAP server error: {}", message),
+        }
+    }
+}
+
 #[derive(Clone)]
 pub struct LdapWrapper {
     ldap_pool: Pool<LdapManager>,
@@ -35,12 +48,13 @@ impl LdapWrapper {
     async fn get_user_infos(&self, uid: &String) -> Result<(bool, Vec<String>), Error> {
         let mut ldap = self.ldap_pool.get().await.unwrap();
         let search_res = ldap.search(&self.config.basedn, ldap3::Scope::Subtree, format!("(uid={})", uid).as_str(), vec!["memberOf", "objectClass"])
-        .await.map_err(|e| {Error::LdapServerError(format!("Error: {}", e))})?;
+        .await.map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
 
-        let (result_entry, _ldap_res) = search_res.success().map_err(|e| {Error::LdapServerError(format!("Error: {}", e))})?;
+        let (result_entry, _ldap_res) = search_res.success()
+        .map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
 
         if result_entry.is_empty() {
-            return Err(Error::LdapServerError(format!("Error contact admins: dn shouldn't be empty")));
+            return Err(Error::LdapServerError {message: format!("Error contact admins: dn shouldn't be empty")});
         }
 
         let records = SearchEntry::construct(result_entry.first().unwrap().to_owned());
@@ -65,16 +79,16 @@ impl LdapWrapper {
         let email = Regex::new(r"^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,6}*").unwrap();
         if email.is_match(&username) {
             let search_res = ldap.search(&self.config.basedn, ldap3::Scope::Subtree, format!("(mail={})", username).as_str(), vec!["uid"])
-            .await.map_err(|e| {Error::LdapServerError(format!("Error: {}", e))})?;
+            .await.map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
 
-            let (result_entry, _ldap_res) = search_res.success().map_err(|e| {Error::LdapServerError(format!("Error: {}", e))})?;
+            let (result_entry, _ldap_res) = search_res.success().map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
 
             if result_entry.is_empty() {
-                return Err(Error::Authfailed(format!("Wrong username or password")));
+                return Err(Error::Authfailed {message: format!("Wrong username or password")});
             }
 
             if result_entry.len() > 1 {
-                return Err(Error::Authfailed(format!("Error contact admins")));
+                return Err(Error::Authfailed {message: format!("Error contact admins")});
             }
 
             let records = SearchEntry::construct(result_entry.first().unwrap().to_owned());
@@ -86,16 +100,15 @@ impl LdapWrapper {
         .simple_bind(format!("uid={},{}", uid, self.config.basedn).as_str(), &passwd)
         .await
         .map_err(|_| {
-            Error::Authfailed(format!("Wrong username or password"))
+            Error::Authfailed {message: format!("Wrong username or password")}
         })
         .and_then(|r| {
             r.success().map_err(|_| {
-                Error::Authfailed(format!("Wrong username or password"))
+                Error::Authfailed {message: format!("Wrong username or password")}
             })
-        })
-        .and({
-            let (is_ssh, groups) = self.get_user_infos(&uid).await?;
-            Ok(User{uid: uid.clone(), is_ssh, groups})})
+        })?;
+        let (is_ssh, groups) = self.get_user_infos(&uid).await?;
+        Ok(User{uid: uid.clone(), is_ssh, groups})
     }
 
     pub async fn change_password(&self, username: String, password: String, new_password: String) -> Result<(), Error> {
@@ -126,12 +139,12 @@ impl LdapWrapper {
         let _ = ldap.simple_bind(format!("uid={},{}", username, self.config.basedn).as_str(), &password)
         .await
         .map_err(|_| {
-            Error::Authfailed(format!("Wrong current password"))
+            Error::Authfailed {message: format!("Wrong current password")}
         }).and_then(|r| {
             r.success().map_err(|_| {
-                Error::Authfailed(format!("Wrong current password"))
+                Error::Authfailed {message: format!("Wrong current password")}
             })
-        });
+        })?;
 
         let hashset= HashSet::from([b64_new_password.as_str()]);
 
@@ -140,13 +153,13 @@ impl LdapWrapper {
         ldap.modify(format!("uid={},{}", username, self.config.basedn).as_str(), data)
         .await
         .map_err(|e| {
-            Error::LdapServerError(format!("An error occured, contact admins: {}", e))
+            Error::LdapServerError {message: format!("An error occured, contact admins: {}", e)}
         })
         .and_then(|r| {
             r.success().map_err(|e| {
-                Error::LdapServerError(format!("An error occured, contact admins: {}", e))
+                Error::LdapServerError {message: format!("An error occured, contact admins: {}", e)}
             })
-        })
-        .and(Ok(()))
+        })?;
+        Ok(())
     }
 }

+ 5 - 0
templates/formpassword.html

@@ -33,5 +33,10 @@
                 </form>
             </div>
         </div>
+        {% if error_message %}
+        <div class="container">
+            <div class="box notification is-warning">{{ error_message }}</div>
+        </div>
+        {% endif %}
     </body>
 </html>

+ 2 - 2
templates/signin.html

@@ -26,9 +26,9 @@
                 </form>
             </div>
         </div>
-        {% if session.message is defined %}
+        {% if error_message %}
             <div class="container">
-                <div class="box notification is-warning">{{ session.message }}</div>
+                <div class="box notification is-warning">{{ error_message }}</div>
             </div>
         {% endif %}
     </body>