From 4e3bb4404098a4dee7c4cb8dc2cfa507c937b249 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Mon, 20 Sep 2021 13:47:08 +0200 Subject: [PATCH] Start the user API --- src/api.rs | 87 +++++++--------- src/api/auth.rs | 85 ++++++++------- src/api/machines.rs | 224 +++++++++++++++++++++------------------- src/api/user.rs | 33 ++++-- src/api/users.rs | 68 ++++++++++-- src/connection.rs | 64 +++++++++--- src/db/user.rs | 34 ++---- src/db/user/internal.rs | 23 ++++- 8 files changed, 364 insertions(+), 254 deletions(-) diff --git a/src/api.rs b/src/api.rs index 7621844..8effc5d 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,3 +1,9 @@ +use std::rc::Rc; +use std::cell::RefCell; +use std::ops::Deref; + +use slog::Logger; + use std::sync::Arc; use capnp::capability::{Params, Results, Promise}; @@ -21,15 +27,19 @@ use users::Users; // TODO Session restoration by making the Bootstrap cap a SturdyRef pub struct Bootstrap { - session: Arc, + log: Logger, + db: Databases, nw: Arc, + + session: Rc>>, } impl Bootstrap { - pub fn new(session: Arc, db: Databases, nw: Arc) -> Self { - info!(session.log, "Created Bootstrap"); - Self { session, db, nw } + pub fn new(log: Logger, db: Databases, nw: Arc) -> Self { + info!(log, "Created Bootstrap"); + 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 // 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(()) } @@ -52,32 +70,19 @@ impl connection_capnp::bootstrap::Server for Bootstrap { _: MachineSystemParams, mut res: MachineSystemResults ) -> Promise<(), capnp::Error> { - let session = self.session.clone(); - let accessdb = self.db.access.clone(); - let nw = self.nw.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)))?; - - debug!(session.log, "Giving MachineSystem cap to user {} with perms:", user.id); - for r in perms.iter() { - debug!(session.log, " {}", r); - } - - // TODO actual permission check and stuff - // Right now we only check that the user has authenticated at all. - let c = capnp_rpc::new_client(Machines::new(user.id, perms, nw)); - res.get().set_machine_system(c); + if let Some(session) = self.session.borrow().deref() { + debug!(self.log, "Giving MachineSystem cap to user {} with perms:", session.authzid); + for r in session.perms.iter() { + debug!(session.log, " {}", r); } - // Promise is Ok either way, just the machine system may not be set, indicating as - // usual a lack of permission. - Ok(()) - }; + // TODO actual permission check and stuff + // Right now we only check that the user has authenticated at all. + let c = capnp_rpc::new_client(Machines::new(Rc::clone(&self.session), self.nw.clone())); + res.get().set_machine_system(c); + } - Promise::from_future(f) + Promise::ok(()) } fn user_system( @@ -85,25 +90,13 @@ impl connection_capnp::bootstrap::Server for Bootstrap { _: UserSystemParams, mut results: UserSystemResults ) -> Promise<(), capnp::Error> { - let session = self.session.clone(); - 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)))?; + if self.session.borrow().is_some() { + // TODO actual permission check and stuff + // Right now we only check that the user has authenticated at all. + let c = capnp_rpc::new_client(Users::new(Rc::clone(&self.session), self.db.userdb.clone())); + results.get().set_user_system(c); + } - // TODO actual permission check and stuff - // Right now we only check that the user has authenticated at all. - let c = capnp_rpc::new_client(Users::new(perms)); - results.get().set_user_system(c); - } - - // Promise is Ok either way, just the machine system may not be set, indicating as - // usual a lack of permission. - Ok(()) - }; - - Promise::from_future(f) + Promise::ok(()) } } diff --git a/src/api/auth.rs b/src/api/auth.rs index 66bc60f..fc84e10 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -4,6 +4,11 @@ //! Authentication using SASL use std::sync::Arc; +use std::rc::Rc; +use std::cell::RefCell; +use std::ops::Deref; + +use slog::Logger; use rsasl::{ SASL, @@ -24,14 +29,15 @@ use crate::api::Session; pub use crate::schema::authenticationsystem_capnp as auth_system; use crate::db::Databases; 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 { passdb: Arc, userdb: Arc, } pub struct SessionData { - session: Arc, + authz: Option, } struct CB; @@ -45,7 +51,6 @@ impl Callback for CB { Property::GSASL_VALIDATE_SIMPLE => { // FIXME: get_property and retrieve_mut can't be used interleaved but that's // technically safe. - let cap_session = session.retrieve_mut().map(|sd| sd.session.clone()); let authid: &str = session .get_property(Property::GSASL_AUTHID) @@ -60,26 +65,13 @@ impl Callback for CB { if let Some(appdata) = sasl.retrieve_mut() { - if let Ok(Some(b)) = appdata.passdb.check(authid, pass.to_bytes()) { - if b { - if let Some(s) = cap_session { - 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 - } - } else { - ReturnCode::GSASL_AUTHENTICATION_ERROR + if let Ok(Some(user)) = appdata.userdb.login(authid, pass.to_bytes()) { + session.retrieve_mut().unwrap().authz.replace(user); + return Ok(()); } - } else { - ReturnCode::GSASL_AUTHENTICATION_ERROR } + + ReturnCode::GSASL_AUTHENTICATION_ERROR } p => { println!("Callback called with property {:?}", p); @@ -90,29 +82,28 @@ impl Callback for CB { } } -pub struct Auth { +pub struct Auth<'a> { pub ctx: RSASL, - session: Arc, + session: Rc>>, + access: Arc, + log: Logger, } -impl Auth { - pub fn new(dbs: Databases, session: Arc) -> Self { +impl<'a> Auth<'a> { + pub fn new(log: Logger, dbs: Databases, session: Rc>>) -> Self { let mut ctx = SASL::new().unwrap(); let appdata = Box::new(AppData { passdb: dbs.passdb.clone(), userdb: dbs.userdb.clone() }); ctx.store(appdata); - ctx.install_callback::(); - info!(session.log, "Auth created"); - - Self { ctx, session } + Self { log, ctx, session, access: dbs.access.clone() } } } use crate::schema::authenticationsystem_capnp::*; -impl authentication_system::Server for Auth { +impl<'a> authentication_system::Server for Auth<'a> { fn mechanisms(&mut self, _: authentication_system::MechanismsParams, 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 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 // TODO: Set the session user. Needs a lookup though <.> + use response::Result as Resres; match step_res { 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(); - outcome.reborrow().set_result(Result::Successful); + outcome.reborrow().set_result(Resres::Successful); if b.len() != 0 { outcome.init_additional_data().set_additional(&b); } @@ -204,11 +211,13 @@ impl authentication_system::Server for Auth { Promise::ok(()) } // TODO: This should really be an outcome because this is failed auth just as much atm. - Err(e) => - return Promise::err(capnp::Error { - kind: capnp::ErrorKind::Failed, - description: format!("SASL error: {}", e), - }), + Err(e) => { + let mut outcome = pry!(res.get().get_response()).init_outcome(); + outcome.reborrow().set_result(Resres::Failed); + let text = format!("{}", e); + outcome.set_help_text(&text); + Promise::ok(()) + } } } diff --git a/src/api/machines.rs b/src/api/machines.rs index 9880f24..46bdfe1 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -1,4 +1,7 @@ use std::sync::Arc; +use std::cell::RefCell; +use std::rc::Rc; +use std::ops::Deref; use capnp::capability::Promise; use capnp::Error; @@ -11,18 +14,18 @@ use crate::schema::machinesystem_capnp::machine_system::info as machines; use crate::network::Network; use crate::db::user::UserId; use crate::db::access::{PermRule, admin_perm}; +use crate::connection::Session; /// An implementation of the `Machines` API #[derive(Clone)] pub struct Machines { - user: UserId, - permissions: Vec, + session: Rc>>, network: Arc, } impl Machines { - pub fn new(user: UserId, permissions: Vec, network: Arc) -> Self { - Self { user, permissions, network } + pub fn new(session: Rc>>, network: Arc) -> Self { + Self { session, network } } } @@ -44,119 +47,128 @@ impl machines::Server for Machines { mut results: machines::GetMachineListResults) -> Promise<(), Error> { - let v: Vec<(String, crate::machine::Machine)> = self.network.machines.iter() - .filter(|(_name, machine)| { - let required_disclose = &machine.desc.privs.disclose; - for perm_rule in self.permissions.iter() { - if perm_rule.match_perm(required_disclose) { - return true; + 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() + .filter(|(_name, machine)| { + let required_disclose = &machine.desc.privs.disclose; + for perm_rule in session.perms.iter() { + if perm_rule.match_perm(required_disclose) { + return true; + } } + + false + }) + .map(|(n,m)| (n.clone(), m.clone())) + .collect(); + + let permissions = &session.perms; + let user = &session.authzid; + + let f = async move { + let mut machines = results.get().init_machine_list(v.len() as u32); + for (i, (name, machine)) in v.into_iter().enumerate() { + let perms = Perms::get_for(&machine.desc.privs, permissions.iter()); + + let mut builder = machines.reborrow().get(i as u32); + builder.set_name(&name); + if let Some(ref desc) = machine.desc.description { + builder.set_description(desc); + } + + let s = match machine.get_status().await { + Status::Free => MachineState::Free, + Status::Disabled => MachineState::Disabled, + Status::Blocked(_) => MachineState::Blocked, + Status::InUse(_) => MachineState::InUse, + Status::Reserved(_) => MachineState::Reserved, + Status::ToCheck(_) => MachineState::ToCheck, + }; + builder.set_state(s); + + let machineapi = Machine::new(user.clone(), perms, machine.clone()); + if perms.write { + builder.set_use(capnp_rpc::new_client(machineapi.clone())); + builder.set_inuse(capnp_rpc::new_client(machineapi.clone())); + } + if perms.manage { + builder.set_transfer(capnp_rpc::new_client(machineapi.clone())); + builder.set_check(capnp_rpc::new_client(machineapi.clone())); + builder.set_manage(capnp_rpc::new_client(machineapi.clone())); + } + if permissions.iter().any(|r| r.match_perm(&admin_perm())) { + builder.set_admin(capnp_rpc::new_client(machineapi.clone())); + } + + builder.set_info(capnp_rpc::new_client(machineapi)); } - false - }) - .map(|(n,m)| (n.clone(), m.clone())) - .collect(); + Ok(()) + }; - let permissions = self.permissions.clone(); - let user = self.user.clone(); - - let f = async move { - let mut machines = results.get().init_machine_list(v.len() as u32); - for (i, (name, machine)) in v.into_iter().enumerate() { - let perms = Perms::get_for(&machine.desc.privs, permissions.iter()); - - let mut builder = machines.reborrow().get(i as u32); - builder.set_name(&name); - if let Some(ref desc) = machine.desc.description { - builder.set_description(desc); - } - - let s = match machine.get_status().await { - Status::Free => MachineState::Free, - Status::Disabled => MachineState::Disabled, - Status::Blocked(_) => MachineState::Blocked, - Status::InUse(_) => MachineState::InUse, - Status::Reserved(_) => MachineState::Reserved, - Status::ToCheck(_) => MachineState::ToCheck, - }; - builder.set_state(s); - - let machineapi = Machine::new(user.clone(), perms, machine.clone()); - if perms.write { - builder.set_use(capnp_rpc::new_client(machineapi.clone())); - builder.set_inuse(capnp_rpc::new_client(machineapi.clone())); - } - if perms.manage { - builder.set_transfer(capnp_rpc::new_client(machineapi.clone())); - builder.set_check(capnp_rpc::new_client(machineapi.clone())); - builder.set_manage(capnp_rpc::new_client(machineapi.clone())); - } - if permissions.iter().any(|r| r.match_perm(&admin_perm())) { - builder.set_admin(capnp_rpc::new_client(machineapi.clone())); - } - - builder.set_info(capnp_rpc::new_client(machineapi)); - } - - Ok(()) - }; - - Promise::from_future(f) + Promise::from_future(f) + } else { + Promise::ok(()) + } } fn get_machine(&mut self, params: machines::GetMachineParams, mut results: machines::GetMachineResults ) -> Promise<(), capnp::Error> { - - let name = { - let params = pry!(params.get()); - pry!(params.get_name()).to_string() - }; - - let network = self.network.clone(); - let user = self.user.clone(); - let permissions = self.permissions.clone(); - - let f = async move { - if let Some(machine) = network.machines.get(&name) { - let mut builder = results.get().init_machine(); - let perms = Perms::get_for(&machine.desc.privs, permissions.iter()); - builder.set_name(&name); - if let Some(ref desc) = machine.desc.description { - builder.set_description(desc); - } - - let machineapi = Machine::new(user.clone(), perms, machine.clone()); - if perms.write { - builder.set_use(capnp_rpc::new_client(machineapi.clone())); - builder.set_inuse(capnp_rpc::new_client(machineapi.clone())); - } - if perms.manage { - builder.set_transfer(capnp_rpc::new_client(machineapi.clone())); - builder.set_check(capnp_rpc::new_client(machineapi.clone())); - builder.set_manage(capnp_rpc::new_client(machineapi.clone())); - } - if permissions.iter().any(|r| r.match_perm(&admin_perm())) { - builder.set_admin(capnp_rpc::new_client(machineapi.clone())); - } - - builder.set_info(capnp_rpc::new_client(machineapi)); - - let s = match machine.get_status().await { - Status::Free => MachineState::Free, - Status::Disabled => MachineState::Disabled, - Status::Blocked(_) => MachineState::Blocked, - Status::InUse(_) => MachineState::InUse, - Status::Reserved(_) => MachineState::Reserved, - Status::ToCheck(_) => MachineState::ToCheck, - }; - builder.set_state(s); + if let Some(session) = self.session.borrow().deref() { + let name = { + let params = pry!(params.get()); + pry!(params.get_name()).to_string() }; - Ok(()) - }; - Promise::from_future(f) + let network = self.network.clone(); + let user = session.authzid; + let permissions = session.perms; + + let f = async move { + if let Some(machine) = network.machines.get(&name) { + let mut builder = results.get().init_machine(); + let perms = Perms::get_for(&machine.desc.privs, permissions.iter()); + builder.set_name(&name); + if let Some(ref desc) = machine.desc.description { + builder.set_description(desc); + } + + let machineapi = Machine::new(user.clone(), perms, machine.clone()); + if perms.write { + builder.set_use(capnp_rpc::new_client(machineapi.clone())); + builder.set_inuse(capnp_rpc::new_client(machineapi.clone())); + } + if perms.manage { + builder.set_transfer(capnp_rpc::new_client(machineapi.clone())); + builder.set_check(capnp_rpc::new_client(machineapi.clone())); + builder.set_manage(capnp_rpc::new_client(machineapi.clone())); + } + if permissions.iter().any(|r| r.match_perm(&admin_perm())) { + builder.set_admin(capnp_rpc::new_client(machineapi.clone())); + } + + builder.set_info(capnp_rpc::new_client(machineapi)); + + let s = match machine.get_status().await { + Status::Free => MachineState::Free, + Status::Disabled => MachineState::Disabled, + Status::Blocked(_) => MachineState::Blocked, + Status::InUse(_) => MachineState::InUse, + Status::Reserved(_) => MachineState::Reserved, + Status::ToCheck(_) => MachineState::ToCheck, + }; + builder.set_state(s); + }; + + Ok(()) + }; + Promise::from_future(f) + } else { + Promise::ok(()) + } } } diff --git a/src/api/user.rs b/src/api/user.rs index 34bf8e5..381d46d 100644 --- a/src/api/user.rs +++ b/src/api/user.rs @@ -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::schema::user_capnp::user::*; #[derive(Clone)] pub struct User { - user: db::User, - perms: Vec, + session: Rc>>, } impl User { - pub fn new(user: db::User, perms: Vec) -> Self { - Self { user, perms } + pub fn new(session: Rc>>) -> Self { + Self { session } } - pub fn fill(&self, builder: &mut Builder) { - builder.set_username(&self.user.id.uid); - if let Some(ref realm) = &self.user.id.realm { + pub fn fill_self(&self, builder: &mut Builder) { + if let Some(session) = self.session.borrow().deref() { + 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(); space.set_name(&realm); } } } -impl info::Server for User {} +impl info::Server for User { + +} impl manage::Server for User {} impl admin::Server for User {} diff --git a/src/api/users.rs b/src/api/users.rs index ad5cce3..eaa24ae 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -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 crate::api::user::User; +use crate::connection::Session; 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::{info, manage}; +use crate::error; #[derive(Clone, Debug)] pub struct Users { - perms: Vec, + session: Rc>>, + userdb: Arc, } impl Users { - pub fn new(perms: Vec) -> Self { - Self { perms } + pub fn new(session: Rc>>, userdb: Arc) -> Self { + Self { session, userdb } } } @@ -31,10 +41,12 @@ impl user_system::Server for Users { mut results: user_system::ManageResults, ) -> Promise<(), capnp::Error> { let perm: &Permission = Permission::new("bffh.users.manage"); - if self.perms.iter().any(|rule| rule.match_perm(perm)) { - results - .get() - .set_manage(capnp_rpc::new_client(self.clone())); + if let Some(session) = self.session.borrow().deref() { + if session.perms.iter().any(|rule| rule.match_perm(perm)) { + results + .get() + .set_manage(capnp_rpc::new_client(self.clone())); + } } Promise::ok(()) @@ -47,8 +59,48 @@ impl info::Server for Users { _: info::GetUserSelfParams, mut results: info::GetUserSelfResults, ) -> 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(()) } } -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> { + }*/ +} diff --git a/src/connection.rs b/src/connection.rs index a3af8ac..e2cbcd5 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,22 +1,22 @@ -use std::sync::Arc; -use std::future::Future; use futures::FutureExt; +use std::future::Future; +use std::sync::Arc; use slog::Logger; use smol::lock::Mutex; use smol::net::TcpStream; -use crate::error::Result; 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::db::access::{AccessControl, PermRule, RoleIdentifier}; +use crate::db::user::UserId; use crate::db::Databases; -use crate::db::access::{AccessControl, Permission}; -use crate::db::user::User; use crate::network::Network; #[derive(Debug)] @@ -25,15 +25,42 @@ use crate::network::Network; pub struct Session { // Session-spezific log pub log: Logger, - pub user: Mutex>, - pub accessdb: Arc, + + /// 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 { - pub fn new(log: Logger, accessdb: Arc) -> Self { - let user = Mutex::new(None); - - Session { log, user, accessdb } + pub fn new( + log: Logger, + authzid: UserId, + 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 } } - pub fn handle(&mut self, stream: TcpStream) -> impl Future> { + pub fn handle(&mut self, stream: TcpStream) -> impl Future> { 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(session, self.db.clone(), self.network.clone()); + let boots = Bootstrap::new(self.log.new(o!()), self.db.clone(), self.network.clone()); let rpc: connection_capnp::bootstrap::Client = capnp_rpc::new_client(boots); - let network = twoparty::VatNetwork::new(stream.clone(), stream, - rpc_twoparty_capnp::Side::Server, Default::default()); + let network = twoparty::VatNetwork::new( + stream.clone(), + stream, + rpc_twoparty_capnp::Side::Server, + Default::default(), + ); let rpc_system = capnp_rpc::RpcSystem::new(Box::new(network), Some(rpc.client)); // Convert the error type to one of our errors diff --git a/src/db/user.rs b/src/db/user.rs index f807776..541eeeb 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -89,6 +89,10 @@ pub struct UserData { /// The higher, the higher the priority. Higher priority users overwrite lower priority ones. pub priority: u64, + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + pub passwd: Option, + /// Additional data storage #[serde(flatten, skip_serializing_if = "HashMap::is_empty")] kv: HashMap, @@ -96,11 +100,7 @@ pub struct UserData { impl UserData { pub fn new(roles: Vec, priority: u64) -> Self { - Self { - roles: roles, - priority: priority, - kv: HashMap::new(), - } + Self { roles, priority, kv: HashMap::new(), passwd: None } } } @@ -124,30 +124,8 @@ pub fn load_file>(path: P) -> Result> { pub fn init(log: Logger, _config: &Config, env: Arc) -> Result { let mut flags = lmdb::DatabaseFlags::empty(); - flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true); - let db = env.create_db(Some("users"), flags)?; + let db = env.create_db(Some("userdb"), flags)?; debug!(&log, "Opened user db successfully."); 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)))); - } -} diff --git a/src/db/user/internal.rs b/src/db/user/internal.rs index 2f3aeb6..fe649a8 100644 --- a/src/db/user/internal.rs +++ b/src/db/user/internal.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use slog::Logger; -use lmdb::{Environment, Transaction, RwTransaction}; +use lmdb::{Environment, Transaction, RwTransaction, Cursor}; use crate::error::Result; @@ -46,4 +46,25 @@ impl Internal { Ok(()) } + + pub fn list_users(&self) -> Result> { + let txn = self.env.begin_ro_txn()?; + Ok(self.list_users_txn(&txn)?.collect()) + } + pub fn list_users_txn(&self, txn: &T) -> Result> { + 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> { + 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) + })) + } }