Bläddra i källkod

adding display of ssh keys

clement 1 år sedan
förälder
incheckning
4923e5e573
4 ändrade filer med 132 tillägg och 2 borttagningar
  1. 52 0
      src/handler.rs
  2. 33 2
      src/ldap/lib.rs
  3. 3 0
      src/main.rs
  4. 44 0
      templates/sshhomepage.html

+ 52 - 0
src/handler.rs

@@ -1,3 +1,5 @@
+use std::collections::HashMap;
+
 use actix_session::Session;
 use actix_web::{http::{header, StatusCode}, web, HttpResponse, Responder};
 use ldap::LdapWrapper;
@@ -25,6 +27,9 @@ async fn get_template(template_name: String, session: Session) -> String {
     .unwrap_or(None);
     let user_is_ssh: Option<bool> = session.get("user_is_ssh")
     .unwrap_or(None);
+    let ssh_keys: Option<HashMap<String, String>> = session.get("ssh_keys")
+    .unwrap_or(None);
+
 
     let tera = Tera::new("templates/*.html")
     .expect("Failed to parse template files");
@@ -45,6 +50,10 @@ async fn get_template(template_name: String, session: Session) -> String {
         Some(user_is_ssh) => ctx.insert("user_is_ssh", &user_is_ssh),
         None => (),
     }
+    match ssh_keys {
+        Some(ssh_keys) => ctx.insert("ssh_keys", &ssh_keys),
+        None => (),
+    }
 
     tera.render(&template_name, &ctx)
     .expect(format!("Faile to render template {}", template_name).as_str())
@@ -161,6 +170,49 @@ pub async fn change_password(ldap_wrapper: web::Data<LdapWrapper>, form: web::Fo
     }
 }
 
+pub async fn form_ssh(ldap_wrapper: web::Data<LdapWrapper>, session: Session) -> impl Responder {
+    if !validate_session(&session) {
+        return HttpResponse::Ok()
+        .status(StatusCode::FOUND)
+        .append_header((header::LOCATION, "/"))
+        .finish();
+    }
+
+    let is_ssh: bool = session.get("user_is_ssh").unwrap_or(None).unwrap();
+    if !is_ssh {
+        return HttpResponse::Ok()
+        .status(StatusCode::FOUND)
+        .append_header((header::LOCATION, "/"))
+        .finish();
+    }
+
+    let uid = session.get("user_id").unwrap().unwrap();
+
+    match ldap_wrapper.get_ssh_keys(uid).await {
+        Ok(ssh_keys) => {
+            session.insert("ssh_keys", ssh_keys).unwrap();
+        },
+        Err(e) => {
+            session.insert("error_message", e.to_string()).unwrap();
+        },
+    }
+    let body = get_template("sshhomepage.html".to_string(), session).await;
+    HttpResponse::Ok().content_type("text/html")
+    .body(body)
+}
+
+pub async fn add_ssh_key(session: Session) -> impl Responder {
+    let body = get_template("sshhomepage.html".to_string(), session).await;
+    HttpResponse::Ok().content_type("text/html")
+    .body(body)
+}
+
+pub async fn del_ssh_key(session: Session) -> impl Responder {
+    let body = get_template("sshhomepage.html".to_string(), session).await;
+    HttpResponse::Ok().content_type("text/html")
+    .body(body)
+}
+
 pub async fn signout(session: Session) -> impl Responder {
     session.purge();
     HttpResponse::Ok()

+ 33 - 2
src/ldap/lib.rs

@@ -1,9 +1,9 @@
 use core::fmt;
-use std::{collections::HashSet, vec};
+use std::{collections::{HashMap, HashSet}, vec};
 use base64::{engine::general_purpose::STANDARD, Engine as _};
 use deadpool::managed::Pool;
 use ldap3::SearchEntry;
-use regex::Regex;
+use regex::{Captures, Regex};
 use ring::rand::{self, SecureRandom};
 use serde::Serialize;
 use sha2::{Sha512, Digest};
@@ -162,4 +162,35 @@ impl LdapWrapper {
         })?;
         Ok(())
     }
+
+    pub async fn get_ssh_keys(&self, username: String) -> Result<HashMap<String, String>, Error> {
+        let mut ldap = self.ldap_pool.get().await.unwrap();
+        let search_res = ldap.search(&self.config.basedn, ldap3::Scope::Subtree, format!("(uid={})", username).as_str(), vec!["ldapPublicKey"])
+        .await.map_err(|e| {Error::LdapServerError {message: 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 {message: format!("Error contact admins: dn shouldn't be empty")});
+        }
+
+        let records = SearchEntry::construct(result_entry.first().unwrap().to_owned());
+
+        let ssh_keys: HashMap<String, String> = match records.attrs.get("sshPublicKey") {
+            Some(keys) => {
+                let mut keys_res = HashMap::new();
+                let key_name_re = Regex::new(r"\S+").unwrap();
+                for key in keys {
+                    let key_split: Vec<Captures> = key_name_re.captures_iter(&key).collect();
+                    let key_name = &key_split[2][0];
+                    keys_res.insert(key_name.to_string(), key.to_string());
+                }
+                keys_res
+            },
+            None => HashMap::new(),
+        };
+
+        Ok(ssh_keys)
+    }
 }

+ 3 - 0
src/main.rs

@@ -52,6 +52,9 @@ async fn main() -> std::io::Result<()> {
         .route("/signout", web::get().to(handler::signout))
         .route("/changepassword", web::get().to(handler::form_password))
         .route("/changepassword", web::post().to(handler::change_password))
+        .route("/ssh", web::get().to(handler::form_ssh))
+        .route("/addsshkey", web::post().to(handler::add_ssh_key))
+        .route("/delsshkey", web::post().to(handler::del_ssh_key))
     })
     .bind(("127.0.0.1", 8080))?
     .run()

+ 44 - 0
templates/sshhomepage.html

@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>BIM</title>
+        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
+    </head>
+    <style>
+        body {word-break: break-all;}
+    </style>
+    <body class="layout-default">
+        <div class="section">
+            {% for key_name, key_value in ssh_keys %}
+                <article class="message is-primary">
+                    <div class="message-header">
+                        <div>{{ key_name }}</div>
+                    </div>
+                    <div class="message-body is-family-monospace">
+                        {{ key_value }} </br></br>
+                        <form method="post" accept-charset="UTF-8" action="delsshkey">
+                        <button type="submit" name="key_to_delete" value="{{ key_value }}" class="button is-danger">Delete</button>
+                        </form>
+                    </div>
+                </article>
+            {% endfor %}
+        </div>
+        <div class="section">
+            <form method="post" accept-charset="UTF-8" action="addsshkey" class="box">
+                <div class="field">
+                    <label for="new_ssh_key" class="label">New SSH key</label>
+                    <div class="control">
+                        <input id="new_ssh_key" type="text" name="new_ssh_key" class="input is-rounded is-primary">
+                    </div>
+                </div>
+
+                <button class="button is-primary">Add</button>
+            </form>
+        </div>
+        {% if session.message is defined %}
+            <div class="container">
+                <div class="box notification is-warning">{{ session.message }}</div>
+            </div>
+        {% endif %}
+    </body>
+</html>