Start the user API

This commit is contained in:
Nadja Reitzenstein 2021-09-20 13:47:08 +02:00
parent 006ae0af68
commit 4e3bb44040
8 changed files with 364 additions and 254 deletions

View File

@ -1,3 +1,9 @@
use std::rc::Rc;
use std::cell::RefCell;
use std::ops::Deref;
use slog::Logger;
use std::sync::Arc; use std::sync::Arc;
use capnp::capability::{Params, Results, Promise}; use capnp::capability::{Params, Results, Promise};
@ -21,15 +27,19 @@ use users::Users;
// TODO Session restoration by making the Bootstrap cap a SturdyRef // TODO Session restoration by making the Bootstrap cap a SturdyRef
pub struct Bootstrap { pub struct Bootstrap {
session: Arc<Session>, log: Logger,
db: Databases, db: Databases,
nw: Arc<Network>, nw: Arc<Network>,
session: Rc<RefCell<Option<Session>>>,
} }
impl Bootstrap { impl Bootstrap {
pub fn new(session: Arc<Session>, db: Databases, nw: Arc<Network>) -> Self { pub fn new(log: Logger, db: Databases, nw: Arc<Network>) -> Self {
info!(session.log, "Created Bootstrap"); info!(log, "Created Bootstrap");
Self { session, db, nw } let session = Rc::new(RefCell::new(None));
Self { session, db, nw, log }
} }
} }
@ -43,7 +53,15 @@ impl connection_capnp::bootstrap::Server for Bootstrap {
// TODO: When should we allow multiple auth and how do me make sure that does not leak // TODO: When should we allow multiple auth and how do me make sure that does not leak
// priviledges (e.g. due to previously issues caps)? // priviledges (e.g. due to previously issues caps)?
res.get().set_authentication_system(capnp_rpc::new_client(auth::Auth::new(self.db.clone(), self.session.clone()))); // If this Rc has a strong count of 1 then there's no other cap issued yet meaning we can
// safely transform the inner session with an auth.
if Rc::strong_count(&self.session) == 1 {
let session = Rc::clone(&self.session);
let db = self.db.clone();
res.get().set_authentication_system(capnp_rpc::new_client(
auth::Auth::new(self.log.new(o!()), db, session))
);
}
Promise::ok(()) Promise::ok(())
} }
@ -52,32 +70,19 @@ impl connection_capnp::bootstrap::Server for Bootstrap {
_: MachineSystemParams, _: MachineSystemParams,
mut res: MachineSystemResults mut res: MachineSystemResults
) -> Promise<(), capnp::Error> { ) -> Promise<(), capnp::Error> {
let session = self.session.clone(); if let Some(session) = self.session.borrow().deref() {
let accessdb = self.db.access.clone(); debug!(self.log, "Giving MachineSystem cap to user {} with perms:", session.authzid);
let nw = self.nw.clone(); for r in session.perms.iter() {
let f = async move {
// Ensure the lock is dropped as soon as possible
if let Some(user) = { session.user.lock().await.clone() } {
let perms = accessdb.collect_permrules(&user.data)
.map_err(|e| capnp::Error::failed(format!("AccessDB lookup failed: {}", e)))?;
debug!(session.log, "Giving MachineSystem cap to user {} with perms:", user.id);
for r in perms.iter() {
debug!(session.log, " {}", r); debug!(session.log, " {}", r);
} }
// TODO actual permission check and stuff // TODO actual permission check and stuff
// Right now we only check that the user has authenticated at all. // Right now we only check that the user has authenticated at all.
let c = capnp_rpc::new_client(Machines::new(user.id, perms, nw)); let c = capnp_rpc::new_client(Machines::new(Rc::clone(&self.session), self.nw.clone()));
res.get().set_machine_system(c); res.get().set_machine_system(c);
} }
// Promise is Ok either way, just the machine system may not be set, indicating as Promise::ok(())
// usual a lack of permission.
Ok(())
};
Promise::from_future(f)
} }
fn user_system( fn user_system(
@ -85,25 +90,13 @@ impl connection_capnp::bootstrap::Server for Bootstrap {
_: UserSystemParams, _: UserSystemParams,
mut results: UserSystemResults mut results: UserSystemResults
) -> Promise<(), capnp::Error> { ) -> Promise<(), capnp::Error> {
let session = self.session.clone(); if self.session.borrow().is_some() {
let accessdb = self.db.access.clone();
let f = async move {
// Ensure the lock is dropped as soon as possible
if let Some(user) = { session.user.lock().await.clone() } {
let perms = accessdb.collect_permrules(&user.data)
.map_err(|e| capnp::Error::failed(format!("AccessDB lookup failed: {}", e)))?;
// TODO actual permission check and stuff // TODO actual permission check and stuff
// Right now we only check that the user has authenticated at all. // Right now we only check that the user has authenticated at all.
let c = capnp_rpc::new_client(Users::new(perms)); let c = capnp_rpc::new_client(Users::new(Rc::clone(&self.session), self.db.userdb.clone()));
results.get().set_user_system(c); results.get().set_user_system(c);
} }
// Promise is Ok either way, just the machine system may not be set, indicating as Promise::ok(())
// usual a lack of permission.
Ok(())
};
Promise::from_future(f)
} }
} }

View File

@ -4,6 +4,11 @@
//! Authentication using SASL //! Authentication using SASL
use std::sync::Arc; use std::sync::Arc;
use std::rc::Rc;
use std::cell::RefCell;
use std::ops::Deref;
use slog::Logger;
use rsasl::{ use rsasl::{
SASL, SASL,
@ -24,14 +29,15 @@ use crate::api::Session;
pub use crate::schema::authenticationsystem_capnp as auth_system; pub use crate::schema::authenticationsystem_capnp as auth_system;
use crate::db::Databases; use crate::db::Databases;
use crate::db::pass::PassDB; use crate::db::pass::PassDB;
use crate::db::user::{Internal as UserDB}; use crate::db::user::{Internal as UserDB, UserId, User};
use crate::db::access::AccessControl as AccessDB;
pub struct AppData { pub struct AppData {
passdb: Arc<PassDB>, passdb: Arc<PassDB>,
userdb: Arc<UserDB>, userdb: Arc<UserDB>,
} }
pub struct SessionData { pub struct SessionData {
session: Arc<Session>, authz: Option<User>,
} }
struct CB; struct CB;
@ -45,7 +51,6 @@ impl Callback<AppData, SessionData> for CB {
Property::GSASL_VALIDATE_SIMPLE => { Property::GSASL_VALIDATE_SIMPLE => {
// FIXME: get_property and retrieve_mut can't be used interleaved but that's // FIXME: get_property and retrieve_mut can't be used interleaved but that's
// technically safe. // technically safe.
let cap_session = session.retrieve_mut().map(|sd| sd.session.clone());
let authid: &str = session let authid: &str = session
.get_property(Property::GSASL_AUTHID) .get_property(Property::GSASL_AUTHID)
@ -60,27 +65,14 @@ impl Callback<AppData, SessionData> for CB {
if let Some(appdata) = sasl.retrieve_mut() { if let Some(appdata) = sasl.retrieve_mut() {
if let Ok(Some(b)) = appdata.passdb.check(authid, pass.to_bytes()) { if let Ok(Some(user)) = appdata.userdb.login(authid, pass.to_bytes()) {
if b { session.retrieve_mut().unwrap().authz.replace(user);
if let Some(s) = cap_session { return Ok(());
if let Ok(Some(user)) = appdata.userdb.get_user(authid) {
// FIXME: This should set the userid outside the callback
s.user.try_lock().unwrap().replace(user);
} }
} }
// Early return, implicitly returns GSASL_OK
return Ok(());
} else {
ReturnCode::GSASL_AUTHENTICATION_ERROR ReturnCode::GSASL_AUTHENTICATION_ERROR
} }
} else {
ReturnCode::GSASL_AUTHENTICATION_ERROR
}
} else {
ReturnCode::GSASL_AUTHENTICATION_ERROR
}
}
p => { p => {
println!("Callback called with property {:?}", p); println!("Callback called with property {:?}", p);
ReturnCode::GSASL_NO_CALLBACK ReturnCode::GSASL_NO_CALLBACK
@ -90,29 +82,28 @@ impl Callback<AppData, SessionData> for CB {
} }
} }
pub struct Auth { pub struct Auth<'a> {
pub ctx: RSASL<AppData, SessionData>, pub ctx: RSASL<AppData, SessionData>,
session: Arc<Session>, session: Rc<RefCell<Option<Session>>>,
access: Arc<AccessDB>,
log: Logger,
} }
impl Auth { impl<'a> Auth<'a> {
pub fn new(dbs: Databases, session: Arc<Session>) -> Self { pub fn new(log: Logger, dbs: Databases, session: Rc<RefCell<Option<Session>>>) -> Self {
let mut ctx = SASL::new().unwrap(); let mut ctx = SASL::new().unwrap();
let appdata = Box::new(AppData { passdb: dbs.passdb.clone(), userdb: dbs.userdb.clone() }); let appdata = Box::new(AppData { passdb: dbs.passdb.clone(), userdb: dbs.userdb.clone() });
ctx.store(appdata); ctx.store(appdata);
ctx.install_callback::<CB>(); ctx.install_callback::<CB>();
info!(session.log, "Auth created"); Self { log, ctx, session, access: dbs.access.clone() }
Self { ctx, session }
} }
} }
use crate::schema::authenticationsystem_capnp::*; use crate::schema::authenticationsystem_capnp::*;
impl authentication_system::Server for Auth { impl<'a> authentication_system::Server for Auth<'a> {
fn mechanisms(&mut self, fn mechanisms(&mut self,
_: authentication_system::MechanismsParams, _: authentication_system::MechanismsParams,
mut res: authentication_system::MechanismsResults mut res: authentication_system::MechanismsResults
@ -166,7 +157,7 @@ impl authentication_system::Server for Auth {
}), }),
}; };
session.store(Box::new(SessionData { session: self.session.clone() })); session.store(Box::new(SessionData { authz: None }));
// If the client has provided initial data go use that // If the client has provided initial data go use that
use request::initial_response::Which; use request::initial_response::Which;
@ -188,12 +179,28 @@ impl authentication_system::Server for Auth {
// The step may either return an error, a success or the need for more data // The step may either return an error, a success or the need for more data
// TODO: Set the session user. Needs a lookup though <.> // TODO: Set the session user. Needs a lookup though <.>
use response::Result as Resres;
match step_res { match step_res {
Ok(Step::Done(b)) => { Ok(Step::Done(b)) => {
use response::Result; let user = session
.retrieve_mut()
.and_then(|data| {
data.authz.take()
})
.expect("Authentication returned OK but didn't set user id");
let perms = pry!(self.access.collect_permrules(&user.data)
.map_err(|e| capnp::Error::failed(format!("AccessDB lookup failed: {}", e))));
self.session.replace(Session::new(
self.log.new(o!()),
user.id,
"".to_string(),
user.data.roles.into_boxed_slice(),
perms.into_boxed_slice()
));
let mut outcome = pry!(res.get().get_response()).init_outcome(); let mut outcome = pry!(res.get().get_response()).init_outcome();
outcome.reborrow().set_result(Result::Successful); outcome.reborrow().set_result(Resres::Successful);
if b.len() != 0 { if b.len() != 0 {
outcome.init_additional_data().set_additional(&b); outcome.init_additional_data().set_additional(&b);
} }
@ -204,11 +211,13 @@ impl authentication_system::Server for Auth {
Promise::ok(()) Promise::ok(())
} }
// TODO: This should really be an outcome because this is failed auth just as much atm. // TODO: This should really be an outcome because this is failed auth just as much atm.
Err(e) => Err(e) => {
return Promise::err(capnp::Error { let mut outcome = pry!(res.get().get_response()).init_outcome();
kind: capnp::ErrorKind::Failed, outcome.reborrow().set_result(Resres::Failed);
description: format!("SASL error: {}", e), let text = format!("{}", e);
}), outcome.set_help_text(&text);
Promise::ok(())
}
} }
} }

View File

@ -1,4 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use std::cell::RefCell;
use std::rc::Rc;
use std::ops::Deref;
use capnp::capability::Promise; use capnp::capability::Promise;
use capnp::Error; use capnp::Error;
@ -11,18 +14,18 @@ use crate::schema::machinesystem_capnp::machine_system::info as machines;
use crate::network::Network; use crate::network::Network;
use crate::db::user::UserId; use crate::db::user::UserId;
use crate::db::access::{PermRule, admin_perm}; use crate::db::access::{PermRule, admin_perm};
use crate::connection::Session;
/// An implementation of the `Machines` API /// An implementation of the `Machines` API
#[derive(Clone)] #[derive(Clone)]
pub struct Machines { pub struct Machines {
user: UserId, session: Rc<RefCell<Option<Session>>>,
permissions: Vec<PermRule>,
network: Arc<Network>, network: Arc<Network>,
} }
impl Machines { impl Machines {
pub fn new(user: UserId, permissions: Vec<PermRule>, network: Arc<Network>) -> Self { pub fn new(session: Rc<RefCell<Option<Session>>>, network: Arc<Network>) -> Self {
Self { user, permissions, network } Self { session, network }
} }
} }
@ -44,10 +47,13 @@ impl machines::Server for Machines {
mut results: machines::GetMachineListResults) mut results: machines::GetMachineListResults)
-> Promise<(), Error> -> Promise<(), Error>
{ {
let session = Rc::clone(&self.session);
let session = session.borrow();
if let Some(session) = session.deref() {
let v: Vec<(String, crate::machine::Machine)> = self.network.machines.iter() let v: Vec<(String, crate::machine::Machine)> = self.network.machines.iter()
.filter(|(_name, machine)| { .filter(|(_name, machine)| {
let required_disclose = &machine.desc.privs.disclose; let required_disclose = &machine.desc.privs.disclose;
for perm_rule in self.permissions.iter() { for perm_rule in session.perms.iter() {
if perm_rule.match_perm(required_disclose) { if perm_rule.match_perm(required_disclose) {
return true; return true;
} }
@ -58,8 +64,8 @@ impl machines::Server for Machines {
.map(|(n,m)| (n.clone(), m.clone())) .map(|(n,m)| (n.clone(), m.clone()))
.collect(); .collect();
let permissions = self.permissions.clone(); let permissions = &session.perms;
let user = self.user.clone(); let user = &session.authzid;
let f = async move { let f = async move {
let mut machines = results.get().init_machine_list(v.len() as u32); let mut machines = results.get().init_machine_list(v.len() as u32);
@ -103,21 +109,24 @@ impl machines::Server for Machines {
}; };
Promise::from_future(f) Promise::from_future(f)
} else {
Promise::ok(())
}
} }
fn get_machine(&mut self, fn get_machine(&mut self,
params: machines::GetMachineParams, params: machines::GetMachineParams,
mut results: machines::GetMachineResults mut results: machines::GetMachineResults
) -> Promise<(), capnp::Error> { ) -> Promise<(), capnp::Error> {
if let Some(session) = self.session.borrow().deref() {
let name = { let name = {
let params = pry!(params.get()); let params = pry!(params.get());
pry!(params.get_name()).to_string() pry!(params.get_name()).to_string()
}; };
let network = self.network.clone(); let network = self.network.clone();
let user = self.user.clone(); let user = session.authzid;
let permissions = self.permissions.clone(); let permissions = session.perms;
let f = async move { let f = async move {
if let Some(machine) = network.machines.get(&name) { if let Some(machine) = network.machines.get(&name) {
@ -158,5 +167,8 @@ impl machines::Server for Machines {
Ok(()) Ok(())
}; };
Promise::from_future(f) Promise::from_future(f)
} else {
Promise::ok(())
}
} }
} }

View File

@ -1,27 +1,42 @@
use crate::db::access::PermRule; use std::rc::Rc;
use std::cell::RefCell;
use std::ops::Deref;
use crate::connection::Session;
use crate::db::user as db; use crate::db::user as db;
use crate::schema::user_capnp::user::*; use crate::schema::user_capnp::user::*;
#[derive(Clone)] #[derive(Clone)]
pub struct User { pub struct User {
user: db::User, session: Rc<RefCell<Option<Session>>>,
perms: Vec<PermRule>,
} }
impl User { impl User {
pub fn new(user: db::User, perms: Vec<PermRule>) -> Self { pub fn new(session: Rc<RefCell<Option<Session>>>) -> Self {
Self { user, perms } Self { session }
} }
pub fn fill(&self, builder: &mut Builder) { pub fn fill_self(&self, builder: &mut Builder) {
builder.set_username(&self.user.id.uid); if let Some(session) = self.session.borrow().deref() {
if let Some(ref realm) = &self.user.id.realm { self.fill_userid(builder, &session.authzid);
}
}
pub fn fill_with(&self, builder: &mut Builder, user: db::User) {
self.fill_userid(builder, &user.id)
}
pub fn fill_userid(&self, builder: &mut Builder, uid: &db::UserId) {
builder.set_username(&uid.uid);
if let Some(ref realm) = &uid.realm {
let mut space = builder.reborrow().init_space(); let mut space = builder.reborrow().init_space();
space.set_name(&realm); space.set_name(&realm);
} }
} }
} }
impl info::Server for User {} impl info::Server for User {
}
impl manage::Server for User {} impl manage::Server for User {}
impl admin::Server for User {} impl admin::Server for User {}

View File

@ -1,17 +1,27 @@
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
use std::ops::Deref;
use capnp::capability::Promise; use capnp::capability::Promise;
use crate::api::user::User;
use crate::connection::Session;
use crate::db::access::{PermRule, Permission}; use crate::db::access::{PermRule, Permission};
use crate::db::user::{UserId, Internal as UserDB};
use crate::schema::usersystem_capnp::user_system; use crate::schema::usersystem_capnp::user_system;
use crate::schema::usersystem_capnp::user_system::{info, manage}; use crate::schema::usersystem_capnp::user_system::{info, manage};
use crate::error;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Users { pub struct Users {
perms: Vec<PermRule>, session: Rc<RefCell<Option<Session>>>,
userdb: Arc<UserDB>,
} }
impl Users { impl Users {
pub fn new(perms: Vec<PermRule>) -> Self { pub fn new(session: Rc<RefCell<Option<Session>>>, userdb: Arc<UserDB>) -> Self {
Self { perms } Self { session, userdb }
} }
} }
@ -31,11 +41,13 @@ impl user_system::Server for Users {
mut results: user_system::ManageResults, mut results: user_system::ManageResults,
) -> Promise<(), capnp::Error> { ) -> Promise<(), capnp::Error> {
let perm: &Permission = Permission::new("bffh.users.manage"); let perm: &Permission = Permission::new("bffh.users.manage");
if self.perms.iter().any(|rule| rule.match_perm(perm)) { if let Some(session) = self.session.borrow().deref() {
if session.perms.iter().any(|rule| rule.match_perm(perm)) {
results results
.get() .get()
.set_manage(capnp_rpc::new_client(self.clone())); .set_manage(capnp_rpc::new_client(self.clone()));
} }
}
Promise::ok(()) Promise::ok(())
} }
@ -47,8 +59,48 @@ impl info::Server for Users {
_: info::GetUserSelfParams, _: info::GetUserSelfParams,
mut results: info::GetUserSelfResults, mut results: info::GetUserSelfResults,
) -> Promise<(), capnp::Error> { ) -> Promise<(), capnp::Error> {
let user = User::new(Rc::clone(&self.session));
let mut builder = results.get().init_user();
user.fill_self(&mut builder);
Promise::ok(()) Promise::ok(())
} }
} }
impl manage::Server for Users {} impl manage::Server for Users {
fn get_user_list(
&mut self,
_: manage::GetUserListParams,
mut results: manage::GetUserListResults,
) -> Promise<(), capnp::Error> {
let result: Result<(), error::Error> =
self.userdb.list_users()
.and_then(|users| {
let builder = results.get().init_user_list(users.len() as u32);
let u = User::new(Rc::clone(&self.session));
for (i, user) in users.into_iter().enumerate() {
let mut b = builder.reborrow().get(i as u32);
u.fill_with(&mut b, user);
}
Ok(())
});
match result {
Ok(()) => Promise::ok(()),
Err(e) => Promise::err(capnp::Error::failed("User lookup failed: {}".to_string())),
}
}
/*fn add_user(
&mut self,
params: manage::AddUserParams,
mut results: manage::AddUserResults,
) -> Promise<(), capnp::Error> {
}
fn remove_user(
&mut self,
_: manage::RemoveUserParams,
mut results: manage::RemoveUserResults,
) -> Promise<(), capnp::Error> {
}*/
}

View File

@ -1,22 +1,22 @@
use std::sync::Arc;
use std::future::Future;
use futures::FutureExt; use futures::FutureExt;
use std::future::Future;
use std::sync::Arc;
use slog::Logger; use slog::Logger;
use smol::lock::Mutex; use smol::lock::Mutex;
use smol::net::TcpStream; use smol::net::TcpStream;
use crate::error::Result;
use crate::api::Bootstrap; use crate::api::Bootstrap;
use crate::error::Result;
use capnp_rpc::{twoparty, rpc_twoparty_capnp}; use capnp_rpc::{rpc_twoparty_capnp, twoparty};
use crate::schema::connection_capnp; use crate::schema::connection_capnp;
use crate::db::access::{AccessControl, PermRule, RoleIdentifier};
use crate::db::user::UserId;
use crate::db::Databases; use crate::db::Databases;
use crate::db::access::{AccessControl, Permission};
use crate::db::user::User;
use crate::network::Network; use crate::network::Network;
#[derive(Debug)] #[derive(Debug)]
@ -25,15 +25,42 @@ use crate::network::Network;
pub struct Session { pub struct Session {
// Session-spezific log // Session-spezific log
pub log: Logger, pub log: Logger,
pub user: Mutex<Option<User>>,
pub accessdb: Arc<AccessControl>, /// User this session has been authorized as.
///
/// Slightly different than the authnid which indicates as what this session has been
/// authenticated as (e.g. using EXTERNAL auth the authnid would be the CN of the client
/// certificate, but the authzid would be an user)
pub authzid: UserId,
pub authnid: String,
/// Roles this session has been assigned via group memberships, direct role assignment or
/// authentication types
pub roles: Box<[RoleIdentifier]>,
/// Permissions this session has.
///
/// This is a snapshot of the permissions the underlying user has
/// take at time of creation (i.e. session establishment)
pub perms: Box<[PermRule]>,
} }
impl Session { impl Session {
pub fn new(log: Logger, accessdb: Arc<AccessControl>) -> Self { pub fn new(
let user = Mutex::new(None); log: Logger,
authzid: UserId,
Session { log, user, accessdb } authnid: String,
roles: Box<[RoleIdentifier]>,
perms: Box<[PermRule]>,
) -> Self {
Session {
log,
authzid,
authnid,
roles,
perms,
}
} }
} }
@ -48,14 +75,17 @@ impl ConnectionHandler {
Self { log, db, network } Self { log, db, network }
} }
pub fn handle(&mut self, stream: TcpStream) -> impl Future<Output=Result<()>> { pub fn handle(&mut self, stream: TcpStream) -> impl Future<Output = Result<()>> {
info!(self.log, "New connection from on {:?}", stream); info!(self.log, "New connection from on {:?}", stream);
let session = Arc::new(Session::new(self.log.new(o!()), self.db.access.clone())); let boots = Bootstrap::new(self.log.new(o!()), self.db.clone(), self.network.clone());
let boots = Bootstrap::new(session, self.db.clone(), self.network.clone());
let rpc: connection_capnp::bootstrap::Client = capnp_rpc::new_client(boots); let rpc: connection_capnp::bootstrap::Client = capnp_rpc::new_client(boots);
let network = twoparty::VatNetwork::new(stream.clone(), stream, let network = twoparty::VatNetwork::new(
rpc_twoparty_capnp::Side::Server, Default::default()); stream.clone(),
stream,
rpc_twoparty_capnp::Side::Server,
Default::default(),
);
let rpc_system = capnp_rpc::RpcSystem::new(Box::new(network), Some(rpc.client)); let rpc_system = capnp_rpc::RpcSystem::new(Box::new(network), Some(rpc.client));
// Convert the error type to one of our errors // Convert the error type to one of our errors

View File

@ -89,6 +89,10 @@ pub struct UserData {
/// The higher, the higher the priority. Higher priority users overwrite lower priority ones. /// The higher, the higher the priority. Higher priority users overwrite lower priority ones.
pub priority: u64, pub priority: u64,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub passwd: Option<String>,
/// Additional data storage /// Additional data storage
#[serde(flatten, skip_serializing_if = "HashMap::is_empty")] #[serde(flatten, skip_serializing_if = "HashMap::is_empty")]
kv: HashMap<String, String>, kv: HashMap<String, String>,
@ -96,11 +100,7 @@ pub struct UserData {
impl UserData { impl UserData {
pub fn new(roles: Vec<RoleIdentifier>, priority: u64) -> Self { pub fn new(roles: Vec<RoleIdentifier>, priority: u64) -> Self {
Self { Self { roles, priority, kv: HashMap::new(), passwd: None }
roles: roles,
priority: priority,
kv: HashMap::new(),
}
} }
} }
@ -124,30 +124,8 @@ pub fn load_file<P: AsRef<Path>>(path: P) -> Result<HashMap<String, User>> {
pub fn init(log: Logger, _config: &Config, env: Arc<lmdb::Environment>) -> Result<Internal> { pub fn init(log: Logger, _config: &Config, env: Arc<lmdb::Environment>) -> Result<Internal> {
let mut flags = lmdb::DatabaseFlags::empty(); let mut flags = lmdb::DatabaseFlags::empty();
flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true); let db = env.create_db(Some("userdb"), flags)?;
let db = env.create_db(Some("users"), flags)?;
debug!(&log, "Opened user db successfully."); debug!(&log, "Opened user db successfully.");
Ok(Internal::new(log, env, db)) Ok(Internal::new(log, env, db))
} }
#[cfg(test_DISABLED)]
mod tests {
use super::*;
#[test]
fn format_uid_test() {
let uid = "testuser".to_string();
let suid = "testsuid".to_string();
let realm = "testloc".to_string();
assert_eq!("testuser",
format!("{}", UserIdentifier::new(uid.clone(), None, None)));
assert_eq!("testuser+testsuid",
format!("{}", UserIdentifier::new(uid.clone(), Some(suid.clone()), None)));
assert_eq!("testuser+testsuid",
format!("{}", UserIdentifier::new(uid.clone(), Some(suid.clone()), None)));
assert_eq!("testuser+testsuid@testloc",
format!("{}", UserIdentifier::new(uid, Some(suid), Some(realm))));
}
}

View File

@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use slog::Logger; use slog::Logger;
use lmdb::{Environment, Transaction, RwTransaction}; use lmdb::{Environment, Transaction, RwTransaction, Cursor};
use crate::error::Result; use crate::error::Result;
@ -46,4 +46,25 @@ impl Internal {
Ok(()) Ok(())
} }
pub fn list_users(&self) -> Result<Vec<User>> {
let txn = self.env.begin_ro_txn()?;
Ok(self.list_users_txn(&txn)?.collect())
}
pub fn list_users_txn<T: Transaction>(&self, txn: &T) -> Result<impl Iterator<Item=User>> {
let mut cursor = txn.open_ro_cursor(self.db)?;
Ok(cursor.iter_start().map(|buf| {
let (_kbuf, vbuf) = buf.unwrap();
flexbuffers::from_slice(vbuf).unwrap()
}))
}
pub fn login(&self, uid: &str, password: &[u8]) -> Result<Option<User>> {
let txn = self.env.begin_ro_txn()?;
Ok(self.get_user_txn(&txn, uid)?
.filter(|user| {
user.data.passwd.is_some()
&& argon2::verify_encoded(user.data.passwd.as_ref().unwrap(), password).unwrap_or(false)
}))
}
} }