From 46e3552e04f2f1f7603797573745b4d5d96ad077 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 27 Apr 2022 20:19:04 +0200 Subject: [PATCH] Implements a first bit of User management. --- bffhd/capnp/user.rs | 27 ++++++++++----------- bffhd/capnp/user_system.rs | 48 +++++++++++++++++++++++++++++--------- bffhd/users/db.rs | 25 ++++++++++++++++++++ bffhd/users/mod.rs | 5 ++++ bin/bffhd/main.rs | 2 +- 5 files changed, 82 insertions(+), 25 deletions(-) diff --git a/bffhd/capnp/user.rs b/bffhd/capnp/user.rs index ad35093..8442301 100644 --- a/bffhd/capnp/user.rs +++ b/bffhd/capnp/user.rs @@ -25,31 +25,31 @@ impl User { pub fn build_optional(&self, user: Option, builder: optional::Builder) { if let Some(user) = user.and_then(|u| self.session.users.get_user(u.get_username())) { let builder = builder.init_just(); - self.fill(user, builder); + Self::fill(&self.session, user, builder); } } pub fn build(session: SessionHandle, builder: user::Builder) { let this = Self::new_self(session); let user = this.session.get_user(); - this.fill(user, builder); + Self::fill(&this.session, user, builder); } - pub fn fill(&self, user: db::User, mut builder: user::Builder) { + pub fn fill(session: &SessionHandle, user: db::User, mut builder: user::Builder) { builder.set_username(user.id.as_str()); - let client = Self::new(self.session.clone(), UserRef::new(user.id.clone())); - // We have permissions on ourself - let is_me = &self.session.get_user_ref().id == &user.id; + let is_me = &session.get_user_ref().id == &user.id; - if is_me || self.session.has_perm(Permission::new("bffh.users.info")) { + let client = Self::new(session.clone(), UserRef::new(user.id)); + + if is_me || session.has_perm(Permission::new("bffh.users.info")) { builder.set_info(capnp_rpc::new_client(client.clone())); } if is_me { builder.set_manage(capnp_rpc::new_client(client.clone())); } - if self.session.has_perm(Permission::new("bffh.users.admin")) { + if session.has_perm(Permission::new("bffh.users.admin")) { builder.set_admin(capnp_rpc::new_client(client.clone())); } } @@ -61,11 +61,12 @@ impl info::Server for User { _: info::ListRolesParams, mut result: info::ListRolesResults, ) -> Promise<(), ::capnp::Error> { - let user = self.session.get_user(); - let mut builder = result.get().init_roles(user.userdata.roles.len() as u32); - for (i, role) in user.userdata.roles.into_iter().enumerate() { - let mut b = builder.reborrow().get(i as u32); - b.set_name(role.as_str()); + if let Some(user) = self.session.users.get_user(self.user.get_username()) { + let mut builder = result.get().init_roles(user.userdata.roles.len() as u32); + for (i, role) in user.userdata.roles.into_iter().enumerate() { + let mut b = builder.reborrow().get(i as u32); + b.set_name(role.as_str()); + } } Promise::ok(()) } diff --git a/bffhd/capnp/user_system.rs b/bffhd/capnp/user_system.rs index eebd246..aaf7da9 100644 --- a/bffhd/capnp/user_system.rs +++ b/bffhd/capnp/user_system.rs @@ -7,6 +7,7 @@ use api::usersystem_capnp::user_system::{ use crate::capnp::user::User; use crate::session::SessionHandle; +use crate::users::db; #[derive(Clone)] @@ -42,28 +43,53 @@ impl manage::Server for Users { let users = pry!(userdb.get_all() .map_err(|e| capnp::Error::failed(format!("UserDB error: {:?}", e)))); let mut builder = result.get().init_user_list(users.len() as u32); - let me = User::new_self(self.session.clone()); for (i, (_, user)) in users.into_iter().enumerate() { - me.fill(user, builder.reborrow().get(i as u32)); + User::fill(&self.session, user, builder.reborrow().get(i as u32)); } Promise::ok(()) } fn add_user( &mut self, - _: manage::AddUserParams, - _: manage::AddUserResults, + params: manage::AddUserParams, + mut result: manage::AddUserResults, ) -> Promise<(), ::capnp::Error> { - Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + let params = pry!(params.get()); + let username = pry!(params.get_username()); + let password = pry!(params.get_password()); + // FIXME: saslprep passwords & usernames before storing them + + if !username.is_empty() && !password.is_empty() { + if self.session.users.get_user(username).is_none() { + let user = db::User::new_with_plain_pw(username, password); + self.session.users.put_user(username, &user); + let mut builder = result.get(); + User::fill(&self.session, user, builder); + } else { + tracing::warn!("Failed to add user: Username taken"); + } + } else { + if username.is_empty() { + tracing::warn!("Failed to add user: Username empty"); + } else if password.is_empty() { + tracing::warn!("Failed to add user: Password empty"); + } + } + + Promise::ok(()) } fn remove_user( &mut self, - _: manage::RemoveUserParams, + params: manage::RemoveUserParams, _: manage::RemoveUserResults, ) -> Promise<(), ::capnp::Error> { - Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + let who: &str = pry!(pry!(params.get()).get_username()); + + if let Err(e) = self.session.users.del_user(who) { + tracing::warn!("Failed to delete user: {:?}", e); + } else { + tracing::info!("Deleted user {}", who); + } + + Promise::ok(()) } } diff --git a/bffhd/users/db.rs b/bffhd/users/db.rs index 1779478..295aa18 100644 --- a/bffhd/users/db.rs +++ b/bffhd/users/db.rs @@ -36,6 +36,22 @@ impl User { Ok(false) } } + + pub fn new_with_plain_pw(username: &str, password: impl AsRef<[u8]>) -> Self { + let config = argon2::Config::default(); + let salt: [u8; 16] = rand::random(); + let hash = argon2::hash_encoded(password.as_ref(), &salt, &config) + .expect(&format!("Failed to hash password for {}: ", username)); + tracing::debug!("Hashed pw for {} to {}", username, hash); + + User { + id: username.to_string(), + userdata: UserData { + passwd: Some(hash), + .. Default::default() + } + } + } } #[derive( @@ -43,6 +59,7 @@ Clone, PartialEq, Eq, Debug, +Default, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, @@ -73,6 +90,7 @@ impl UserData { pub fn new_with_kv(roles: Vec, kv: HashMap) -> Self { Self { roles, kv, passwd: None } } + } #[derive(Clone, Debug)] @@ -116,6 +134,13 @@ impl UserDB { Ok(()) } + pub fn delete(&self, uid: &str) -> Result<(), db::Error> { + let mut txn = self.env.begin_rw_txn()?; + self.db.del(&mut txn, &uid)?; + txn.commit()?; + Ok(()) + } + pub fn get_all(&self) -> Result, db::Error> { let txn = self.env.begin_ro_txn()?; let iter = self.db.get_all(&txn)?; diff --git a/bffhd/users/mod.rs b/bffhd/users/mod.rs index 5ba84c5..8941563 100644 --- a/bffhd/users/mod.rs +++ b/bffhd/users/mod.rs @@ -98,6 +98,11 @@ impl Users { self.userdb.put(uid, user) } + pub fn del_user(&self, uid: &str) -> Result<(), lmdb::Error> { + tracing::trace!(uid, "Deleting user"); + self.userdb.delete(uid) + } + pub fn load_file>(&self, path: P) -> anyhow::Result<()> { let f = std::fs::read(path)?; let map: HashMap = toml::from_slice(&f)?; diff --git a/bin/bffhd/main.rs b/bin/bffhd/main.rs index 4cb26ec..4a66bc8 100644 --- a/bin/bffhd/main.rs +++ b/bin/bffhd/main.rs @@ -1,4 +1,4 @@ -use clap::{Arg, Command, Parser}; +use clap::{Arg, Command}; use diflouroborane::{config, Diflouroborane};