mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-21 22:47:55 +01:00
Implements a first bit of User management.
This commit is contained in:
parent
fe992a9446
commit
46e3552e04
@ -25,31 +25,31 @@ impl User {
|
|||||||
pub fn build_optional(&self, user: Option<UserRef>, builder: optional::Builder<user::Owned>) {
|
pub fn build_optional(&self, user: Option<UserRef>, builder: optional::Builder<user::Owned>) {
|
||||||
if let Some(user) = user.and_then(|u| self.session.users.get_user(u.get_username())) {
|
if let Some(user) = user.and_then(|u| self.session.users.get_user(u.get_username())) {
|
||||||
let builder = builder.init_just();
|
let builder = builder.init_just();
|
||||||
self.fill(user, builder);
|
Self::fill(&self.session, user, builder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(session: SessionHandle, builder: user::Builder) {
|
pub fn build(session: SessionHandle, builder: user::Builder) {
|
||||||
let this = Self::new_self(session);
|
let this = Self::new_self(session);
|
||||||
let user = this.session.get_user();
|
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());
|
builder.set_username(user.id.as_str());
|
||||||
|
|
||||||
let client = Self::new(self.session.clone(), UserRef::new(user.id.clone()));
|
|
||||||
|
|
||||||
// We have permissions on ourself
|
// 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()));
|
builder.set_info(capnp_rpc::new_client(client.clone()));
|
||||||
}
|
}
|
||||||
if is_me {
|
if is_me {
|
||||||
builder.set_manage(capnp_rpc::new_client(client.clone()));
|
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()));
|
builder.set_admin(capnp_rpc::new_client(client.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,11 +61,12 @@ impl info::Server for User {
|
|||||||
_: info::ListRolesParams,
|
_: info::ListRolesParams,
|
||||||
mut result: info::ListRolesResults,
|
mut result: info::ListRolesResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
let user = self.session.get_user();
|
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);
|
let mut builder = result.get().init_roles(user.userdata.roles.len() as u32);
|
||||||
for (i, role) in user.userdata.roles.into_iter().enumerate() {
|
for (i, role) in user.userdata.roles.into_iter().enumerate() {
|
||||||
let mut b = builder.reborrow().get(i as u32);
|
let mut b = builder.reborrow().get(i as u32);
|
||||||
b.set_name(role.as_str());
|
b.set_name(role.as_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ use api::usersystem_capnp::user_system::{
|
|||||||
use crate::capnp::user::User;
|
use crate::capnp::user::User;
|
||||||
|
|
||||||
use crate::session::SessionHandle;
|
use crate::session::SessionHandle;
|
||||||
|
use crate::users::db;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -42,28 +43,53 @@ impl manage::Server for Users {
|
|||||||
let users = pry!(userdb.get_all()
|
let users = pry!(userdb.get_all()
|
||||||
.map_err(|e| capnp::Error::failed(format!("UserDB error: {:?}", e))));
|
.map_err(|e| capnp::Error::failed(format!("UserDB error: {:?}", e))));
|
||||||
let mut builder = result.get().init_user_list(users.len() as u32);
|
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() {
|
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(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
fn add_user(
|
fn add_user(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: manage::AddUserParams,
|
params: manage::AddUserParams,
|
||||||
_: manage::AddUserResults,
|
mut result: manage::AddUserResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
Promise::err(::capnp::Error::unimplemented(
|
let params = pry!(params.get());
|
||||||
"method not implemented".to_string(),
|
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(
|
fn remove_user(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: manage::RemoveUserParams,
|
params: manage::RemoveUserParams,
|
||||||
_: manage::RemoveUserResults,
|
_: manage::RemoveUserResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
Promise::err(::capnp::Error::unimplemented(
|
let who: &str = pry!(pry!(params.get()).get_username());
|
||||||
"method not implemented".to_string(),
|
|
||||||
))
|
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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,22 @@ impl User {
|
|||||||
Ok(false)
|
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(
|
#[derive(
|
||||||
@ -43,6 +59,7 @@ Clone,
|
|||||||
PartialEq,
|
PartialEq,
|
||||||
Eq,
|
Eq,
|
||||||
Debug,
|
Debug,
|
||||||
|
Default,
|
||||||
rkyv::Archive,
|
rkyv::Archive,
|
||||||
rkyv::Serialize,
|
rkyv::Serialize,
|
||||||
rkyv::Deserialize,
|
rkyv::Deserialize,
|
||||||
@ -73,6 +90,7 @@ impl UserData {
|
|||||||
pub fn new_with_kv(roles: Vec<String>, kv: HashMap<String, String>) -> Self {
|
pub fn new_with_kv(roles: Vec<String>, kv: HashMap<String, String>) -> Self {
|
||||||
Self { roles, kv, passwd: None }
|
Self { roles, kv, passwd: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -116,6 +134,13 @@ impl UserDB {
|
|||||||
Ok(())
|
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<Vec<(String, User)>, db::Error> {
|
pub fn get_all(&self) -> Result<Vec<(String, User)>, db::Error> {
|
||||||
let txn = self.env.begin_ro_txn()?;
|
let txn = self.env.begin_ro_txn()?;
|
||||||
let iter = self.db.get_all(&txn)?;
|
let iter = self.db.get_all(&txn)?;
|
||||||
|
@ -98,6 +98,11 @@ impl Users {
|
|||||||
self.userdb.put(uid, user)
|
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<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
|
pub fn load_file<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
|
||||||
let f = std::fs::read(path)?;
|
let f = std::fs::read(path)?;
|
||||||
let map: HashMap<String, UserData> = toml::from_slice(&f)?;
|
let map: HashMap<String, UserData> = toml::from_slice(&f)?;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use clap::{Arg, Command, Parser};
|
use clap::{Arg, Command};
|
||||||
use diflouroborane::{config, Diflouroborane};
|
use diflouroborane::{config, Diflouroborane};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user