mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-22 06:47:56 +01:00
Start the user API
This commit is contained in:
parent
006ae0af68
commit
4e3bb44040
87
src/api.rs
87
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<Session>,
|
||||
log: Logger,
|
||||
|
||||
db: Databases,
|
||||
nw: Arc<Network>,
|
||||
|
||||
session: Rc<RefCell<Option<Session>>>,
|
||||
}
|
||||
|
||||
impl Bootstrap {
|
||||
pub fn new(session: Arc<Session>, db: Databases, nw: Arc<Network>) -> Self {
|
||||
info!(session.log, "Created Bootstrap");
|
||||
Self { session, db, nw }
|
||||
pub fn new(log: Logger, db: Databases, nw: Arc<Network>) -> 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(())
|
||||
}
|
||||
}
|
||||
|
@ -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<PassDB>,
|
||||
userdb: Arc<UserDB>,
|
||||
}
|
||||
pub struct SessionData {
|
||||
session: Arc<Session>,
|
||||
authz: Option<User>,
|
||||
}
|
||||
|
||||
struct CB;
|
||||
@ -45,7 +51,6 @@ impl Callback<AppData, SessionData> 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<AppData, SessionData> 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<AppData, SessionData> for CB {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Auth {
|
||||
pub struct Auth<'a> {
|
||||
pub ctx: RSASL<AppData, SessionData>,
|
||||
session: Arc<Session>,
|
||||
session: Rc<RefCell<Option<Session>>>,
|
||||
access: Arc<AccessDB>,
|
||||
log: Logger,
|
||||
}
|
||||
|
||||
impl Auth {
|
||||
pub fn new(dbs: Databases, session: Arc<Session>) -> Self {
|
||||
impl<'a> Auth<'a> {
|
||||
pub fn new(log: Logger, dbs: Databases, session: Rc<RefCell<Option<Session>>>) -> 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::<CB>();
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<PermRule>,
|
||||
session: Rc<RefCell<Option<Session>>>,
|
||||
network: Arc<Network>,
|
||||
}
|
||||
|
||||
impl Machines {
|
||||
pub fn new(user: UserId, permissions: Vec<PermRule>, network: Arc<Network>) -> Self {
|
||||
Self { user, permissions, network }
|
||||
pub fn new(session: Rc<RefCell<Option<Session>>>, network: Arc<Network>) -> 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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<PermRule>,
|
||||
session: Rc<RefCell<Option<Session>>>,
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub fn new(user: db::User, perms: Vec<PermRule>) -> Self {
|
||||
Self { user, perms }
|
||||
pub fn new(session: Rc<RefCell<Option<Session>>>) -> 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 {}
|
||||
|
@ -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<PermRule>,
|
||||
session: Rc<RefCell<Option<Session>>>,
|
||||
userdb: Arc<UserDB>,
|
||||
}
|
||||
|
||||
impl Users {
|
||||
pub fn new(perms: Vec<PermRule>) -> Self {
|
||||
Self { perms }
|
||||
pub fn new(session: Rc<RefCell<Option<Session>>>, userdb: Arc<UserDB>) -> 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> {
|
||||
}*/
|
||||
}
|
||||
|
@ -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<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 {
|
||||
pub fn new(log: Logger, accessdb: Arc<AccessControl>) -> 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<Output=Result<()>> {
|
||||
pub fn handle(&mut self, stream: TcpStream) -> impl Future<Output = Result<()>> {
|
||||
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
|
||||
|
@ -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<String>,
|
||||
|
||||
/// Additional data storage
|
||||
#[serde(flatten, skip_serializing_if = "HashMap::is_empty")]
|
||||
kv: HashMap<String, String>,
|
||||
@ -96,11 +100,7 @@ pub struct UserData {
|
||||
|
||||
impl UserData {
|
||||
pub fn new(roles: Vec<RoleIdentifier>, 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<P: AsRef<Path>>(path: P) -> Result<HashMap<String, User>> {
|
||||
|
||||
pub fn init(log: Logger, _config: &Config, env: Arc<lmdb::Environment>) -> Result<Internal> {
|
||||
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))));
|
||||
}
|
||||
}
|
||||
|
@ -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<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)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user