More API implementation

This commit is contained in:
Nadja Reitzenstein 2021-09-18 17:01:35 +02:00
parent b8a9b64953
commit e5903961d1
6 changed files with 249 additions and 160 deletions

View File

@ -6,6 +6,7 @@ use crate::schema::connection_capnp;
use crate::connection::Session; use crate::connection::Session;
use crate::db::Databases; use crate::db::Databases;
use crate::db::user::UserId;
use crate::network::Network; use crate::network::Network;
@ -55,11 +56,27 @@ 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();
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)))?;
// TODO actual permission check and stuff // TODO actual permission check and stuff
let c = capnp_rpc::new_client(Machines::new(self.session.clone(), self.db.clone(), self.nw.clone())); // 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); res.get().set_machine_system(c);
}
Promise::ok(()) // 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)
} }
} }

View File

@ -64,6 +64,7 @@ impl Callback<AppData, SessionData> for CB {
if b { if b {
if let Some(s) = cap_session { if let Some(s) = cap_session {
if let Ok(Some(user)) = appdata.userdb.get_user(authid) { 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); s.user.try_lock().unwrap().replace(user);
} }
} }
@ -116,7 +117,7 @@ impl authentication_system::Server for Auth {
_: authentication_system::MechanismsParams, _: authentication_system::MechanismsParams,
mut res: authentication_system::MechanismsResults mut res: authentication_system::MechanismsResults
) -> Promise<(), capnp::Error> { ) -> Promise<(), capnp::Error> {
let mechs = match self.ctx.server_mech_list() { /*let mechs = match self.ctx.server_mech_list() {
Ok(m) => m, Ok(m) => m,
Err(e) => { Err(e) => {
return Promise::err(capnp::Error { return Promise::err(capnp::Error {
@ -131,7 +132,10 @@ impl authentication_system::Server for Auth {
let mut res_mechs = res.get().init_mechs(mechvec.len() as u32); let mut res_mechs = res.get().init_mechs(mechvec.len() as u32);
for (i, m) in mechvec.into_iter().enumerate() { for (i, m) in mechvec.into_iter().enumerate() {
res_mechs.set(i as u32, m); res_mechs.set(i as u32, m);
} }*/
// For now, only PLAIN
let mut res_mechs = res.get().init_mechs(1);
res_mechs.set(0, "PLAIN");
Promise::ok(()) Promise::ok(())
} }
@ -146,6 +150,13 @@ impl authentication_system::Server for Auth {
// Extract the MECHANISM the client wants to use and start a session. // Extract the MECHANISM the client wants to use and start a session.
// Or fail at that and thrown an exception TODO: return Outcome // Or fail at that and thrown an exception TODO: return Outcome
let mech = pry!(req.get_mechanism()); let mech = pry!(req.get_mechanism());
if pry!(req.get_mechanism()) != "PLAIN" {
return Promise::err(capnp::Error {
kind: capnp::ErrorKind::Failed,
description: format!("Invalid SASL mech"),
})
}
let mut session = match self.ctx.server_start(mech) { let mut session = match self.ctx.server_start(mech) {
Ok(s) => s, Ok(s) => s,
Err(e) => Err(e) =>

View File

@ -1,137 +1,115 @@
use std::sync::Arc; use std::sync::Arc;
use std::ops::Deref;
use capnp::capability::Promise; use capnp::capability::Promise;
use capnp::Error; use capnp::Error;
use futures::FutureExt; use futures::FutureExt;
use crate::schema::machine_capnp::machine::*; use crate::db::access::{PrivilegesBuf, PermRule};
use crate::connection::Session;
use crate::db::Databases; use crate::db::machine::Status;
use crate::db::machine::{Status, MachineState}; use crate::machine::Machine as NwMachine;
use crate::machine::{Machine as NwMachine, ReturnToken}; use crate::schema::machine_capnp::machine::*;
#[derive(Clone, Copy)]
pub struct Perms {
pub disclose: bool,
pub read: bool,
pub write: bool,
pub manage: bool,
}
impl Perms {
pub fn get_for<'a, I: Iterator<Item=&'a PermRule>>(privs: &'a PrivilegesBuf, rules: I) -> Self {
let mut disclose = false;
let mut read = false;
let mut write = false;
let mut manage = false;
for rule in rules {
if rule.match_perm(&privs.disclose) {
disclose = true;
}
if rule.match_perm(&privs.read) {
read = true;
}
if rule.match_perm(&privs.write) {
write = true;
}
if rule.match_perm(&privs.manage) {
manage = true;
}
}
Self { disclose, read, write, manage }
}
}
#[derive(Clone)]
pub struct Machine { pub struct Machine {
session: Arc<Session>, perms: Perms,
machine: NwMachine, machine: NwMachine,
db: Databases,
} }
impl Machine { impl Machine {
pub fn new(session: Arc<Session>, machine: NwMachine, db: Databases) -> Self { pub fn new(perms: Perms, machine: NwMachine) -> Self {
Machine { session, machine, db } Self { perms, machine }
}
pub fn fill(self: Arc<Self>, builder: &mut Builder) {
builder.set_manage(capnp_rpc::new_client(Manage(self.clone())));
builder.set_admin(capnp_rpc::new_client(Admin(self.clone())));
} }
} }
#[derive(Clone)] impl info::Server for Machine {
pub struct Read(Arc<Machine>); fn get_machine_info_extended(
&mut self,
_: info::GetMachineInfoExtendedParams,
_results: info::GetMachineInfoExtendedResults,
) -> capnp::capability::Promise<(), capnp::Error> {
/*if self.perms.manage {
let mut builder = results.get();
let mut extinfo = builder.init_machine_info_extended();
let mut current = extinfo.init_current_user();
// FIXME fill user
}
Promise::ok(())*/
impl Read { Promise::err(capnp::Error::unimplemented("Extended Infos are unavailable".to_string()))
pub fn new(inner: Arc<Machine>) -> Self { }
Self(inner)
fn get_reservation_list(
&mut self,
_: info::GetReservationListParams,
mut results: info::GetReservationListResults,
) -> capnp::capability::Promise<(), capnp::Error> {
Promise::err(capnp::Error::unimplemented("Reservations are unavailable".to_string()))
}
fn get_property_list(
&mut self,
_: info::GetPropertyListParams,
mut results: info::GetPropertyListResults,
) -> capnp::capability::Promise<(), capnp::Error> {
Promise::err(capnp::Error::unimplemented("Extended Properties are unavailable".to_string()))
} }
} }
impl info::Server for Read { impl use_::Server for Machine {
} fn use_(
&mut self,
struct Write(Arc<Machine>); _: use_::UseParams,
_: use_::UseResults
impl use_::Server for Write { ) -> capnp::capability::Promise<(), capnp::Error> {
fn use_(&mut self, Promise::ok(())
_params: use_::UseParams,
mut results: use_::UseResults)
-> Promise<(), Error>
{
let uid = self.0.session.user.try_lock().unwrap().as_ref().map(|u| u.id.clone());
let new_state = MachineState::used(uid.clone());
let this = self.0.clone();
let f = async move {
let res_token = this.machine.request_state_change(
this.session.user.try_lock().unwrap().as_ref(),
new_state
).await;
match res_token {
// TODO: Do something with the token we get returned
Ok(tok) => {
return Ok(());
},
Err(e) => Err(capnp::Error::failed(format!("State change request returned {}", e))),
}
};
Promise::from_future(f)
}
fn reserve(&mut self,
_params: use_::ReserveParams,
_results: use_::ReserveResults)
-> Promise<(), Error>
{
unimplemented!()
}
}
impl in_use::Server for Write {
fn give_back(&mut self,
_params: in_use::GiveBackParams,
mut results: in_use::GiveBackResults)
-> Promise<(), Error>
{
let this = self.0.clone();
let f = async move {
let status = this.machine.get_status().await;
let sess = this.session.clone();
match status {
Status::InUse(Some(uid)) => {
let user = sess.user.lock().await;
if let Some(u) = user.as_ref() {
if u.id == uid {
}
}
},
// Machine not in use
_ => {
}
}
};
Promise::from_future(f.map(|_| Ok(())))
} }
} }
struct Manage(Arc<Machine>); impl in_use::Server for Machine {
impl manage::Server for Manage {
} }
struct Admin(Arc<Machine>); impl transfer::Server for Machine {
impl admin::Server for Admin {
fn force_set_state(&mut self,
_params: admin::ForceSetStateParams,
_results: admin::ForceSetStateResults)
-> Promise<(), Error>
{
unimplemented!()
} }
fn force_set_user(&mut self, impl check::Server for Machine {
_params: admin::ForceSetUserParams,
_results: admin::ForceSetUserResults)
-> Promise<(), Error>
{
unimplemented!()
} }
impl manage::Server for Machine {
}
impl admin::Server for Machine {
} }

View File

@ -3,35 +3,39 @@ use std::sync::Arc;
use capnp::capability::Promise; use capnp::capability::Promise;
use capnp::Error; use capnp::Error;
use crate::db::machine::Status;
use crate::api::machine::*;
use crate::schema::machine_capnp::machine::MachineState;
use crate::schema::machinesystem_capnp::machine_system; use crate::schema::machinesystem_capnp::machine_system;
use crate::schema::machinesystem_capnp::machine_system::info as machines; use crate::schema::machinesystem_capnp::machine_system::info as machines;
use crate::connection::Session;
use crate::db::Databases;
use crate::network::Network; use crate::network::Network;
use crate::db::user::UserId;
use super::machine::*; use crate::db::access::{PermRule, admin_perm};
/// An implementation of the `Machines` API /// An implementation of the `Machines` API
#[derive(Clone)]
pub struct Machines { pub struct Machines {
/// A reference to the connection — as long as at least one API endpoint is user: UserId,
/// still alive the session has to be as well. permissions: Vec<PermRule>,
session: Arc<Session>,
db: Databases,
network: Arc<Network>, network: Arc<Network>,
} }
impl Machines { impl Machines {
pub fn new(session: Arc<Session>, db: Databases, network: Arc<Network>) -> Self { pub fn new(user: UserId, permissions: Vec<PermRule>, network: Arc<Network>) -> Self {
info!(session.log, "Machines created"); Self { user, permissions, network }
Self { session, db, network }
} }
} }
impl machine_system::Server for Machines { impl machine_system::Server for Machines {
// This function shouldn't exist. See fabaccess-api issue #16
fn info(&mut self,
_:machine_system::InfoParams,
mut results: machine_system::InfoResults
) -> capnp::capability::Promise<(), capnp::Error>
{
results.get().set_info(capnp_rpc::new_client(self.clone()));
Promise::ok(())
}
} }
impl machines::Server for Machines { impl machines::Server for Machines {
@ -41,18 +45,61 @@ impl machines::Server for Machines {
-> Promise<(), Error> -> Promise<(), Error>
{ {
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)| {
let required_disclose = &machine.desc.privs.disclose;
for perm_rule in self.permissions.iter() {
if perm_rule.match_perm(required_disclose) {
return true;
}
}
false
})
.map(|(n,m)| (n.clone(), m.clone())) .map(|(n,m)| (n.clone(), m.clone()))
.collect(); .collect();
/*let mut machines = results.get().init_machines(v.len() as u32); let permissions = self.permissions.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() { for (i, (name, machine)) in v.into_iter().enumerate() {
trace!(self.session.log, "Adding machine #{} {}: {:?}", i, name, machine); let perms = Perms::get_for(&machine.desc.privs, permissions.iter());
let machine = Arc::new(Machine::new(self.session.clone(), machine, self.db.clone()));
let mut builder = machines.reborrow().get(i as u32);
machine.fill(&mut builder);
}*/
Promise::ok(()) 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);
if perms.write {
builder.set_use(capnp_rpc::new_client(Machine::new(perms, machine.clone())));
builder.set_inuse(capnp_rpc::new_client(Machine::new(perms, machine.clone())));
}
if perms.manage {
builder.set_transfer(capnp_rpc::new_client(Machine::new(perms, machine.clone())));
builder.set_check(capnp_rpc::new_client(Machine::new(perms, machine.clone())));
builder.set_manage(capnp_rpc::new_client(Machine::new(perms, machine.clone())));
}
if permissions.iter().any(|r| r.match_perm(&admin_perm())) {
builder.set_admin(capnp_rpc::new_client(Machine::new(perms, machine.clone())));
}
builder.set_info(capnp_rpc::new_client(Machine::new(perms, machine)));
}
Ok(())
};
Promise::from_future(f)
} }
} }

View File

@ -22,10 +22,15 @@ pub struct AccessControl {
pub internal: Internal, pub internal: Internal,
} }
pub const ADMINPERM: &'static str = "bffh.admin";
pub fn admin_perm() -> &'static Permission {
Permission::new(ADMINPERM)
}
impl AccessControl { impl AccessControl {
pub fn new(internal: Internal) -> Self { pub fn new(internal: Internal) -> Self {
Self { Self {
internal: internal, internal,
} }
} }
@ -47,6 +52,10 @@ impl AccessControl {
return Ok(false); return Ok(false);
} }
pub fn collect_permrules(&self, user: &UserData) -> Result<Vec<PermRule>> {
self.internal.collect_permrules(user)
}
pub fn dump_roles(&self) -> Result<Vec<(RoleIdentifier, Role)>> { pub fn dump_roles(&self) -> Result<Vec<(RoleIdentifier, Role)>> {
self.internal.dump_roles() self.internal.dump_roles()
} }
@ -120,6 +129,22 @@ pub trait RoleDB {
Ok(()) Ok(())
} }
fn collect_permrules(&self, user: &UserData) -> Result<Vec<PermRule>> {
let mut roleset = HashMap::new();
for role_id in user.roles.iter() {
self.tally_role(&mut roleset, role_id)?;
}
let mut output = Vec::new();
// Iter all unique role->permissions we've found and early return on match.
for (_roleid, role) in roleset.iter() {
output.extend(role.permissions.iter().cloned())
}
return Ok(output);
}
} }
/// A "Role" from the Authorization perspective /// A "Role" from the Authorization perspective
@ -310,7 +335,7 @@ impl PermissionBuf {
self.inner.push_str(perm.as_str()) self.inner.push_str(perm.as_str())
} }
pub fn from_string(inner: String) -> Self { pub const fn from_string(inner: String) -> Self {
Self { inner } Self { inner }
} }
@ -429,7 +454,7 @@ pub enum PermRule {
impl PermRule { impl PermRule {
// Does this rule match that permission // Does this rule match that permission
fn match_perm<P: AsRef<Permission>>(&self, perm: &P) -> bool { pub fn match_perm<P: AsRef<Permission>>(&self, perm: &P) -> bool {
match self { match self {
PermRule::Base(ref base) => base.as_permission() == perm.as_ref(), PermRule::Base(ref base) => base.as_permission() == perm.as_ref(),
PermRule::Children(ref parent) => parent.as_permission() > perm.as_ref() , PermRule::Children(ref parent) => parent.as_permission() > perm.as_ref() ,

View File

@ -77,21 +77,19 @@ impl Index {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Machine { pub struct Machine {
pub id: uuid::Uuid, pub id: uuid::Uuid,
pub name: String, pub desc: MachineDescription,
pub description: String,
inner: Arc<Mutex<Inner>>, inner: Arc<Mutex<Inner>>,
access: Arc<access::AccessControl>, access: Arc<access::AccessControl>,
} }
impl Machine { impl Machine {
pub fn new(inner: Inner, access: Arc<access::AccessControl>) -> Self { pub fn new(inner: Inner, desc: MachineDescription, access: Arc<access::AccessControl>) -> Self {
Self { Self {
id: uuid::Uuid::default(), id: uuid::Uuid::default(),
name: "".to_string(),
description: "".to_string(),
inner: Arc::new(Mutex::new(inner)), inner: Arc::new(Mutex::new(inner)),
access: access, access,
desc,
} }
} }
@ -102,7 +100,7 @@ impl Machine {
, access: Arc<access::AccessControl> , access: Arc<access::AccessControl>
) -> Machine ) -> Machine
{ {
Self::new(Inner::new(id, desc, state), access) Self::new(Inner::new(id, state), desc, access)
} }
pub fn from_file<P: AsRef<Path>>(path: P, access: Arc<access::AccessControl>) pub fn from_file<P: AsRef<Path>>(path: P, access: Arc<access::AccessControl>)
@ -129,14 +127,14 @@ impl Machine {
let f = async move { let f = async move {
if let Some(udata) = udata { if let Some(udata) = udata {
let mut guard = this.inner.try_lock().unwrap(); if this.access.check(&udata, &this.desc.privs.write).await? {
if this.access.check(&udata, &guard.desc.privs.write).await? { let mut guard = this.inner.lock().await;
guard.do_state_change(new_state); guard.do_state_change(new_state);
return Ok(ReturnToken::new(this.inner.clone())) return Ok(ReturnToken::new(this.inner.clone()))
} }
} else { } else {
if new_state == MachineState::free() { if new_state == MachineState::free() {
let mut guard = this.inner.try_lock().unwrap(); let mut guard = this.inner.lock().await;
guard.do_state_change(new_state); guard.do_state_change(new_state);
return Ok(ReturnToken::new(this.inner.clone())); return Ok(ReturnToken::new(this.inner.clone()));
} }
@ -148,6 +146,20 @@ impl Machine {
Box::pin(f) Box::pin(f)
} }
pub fn do_state_change(&self, new_state: MachineState)
-> BoxFuture<'static, Result<ReturnToken>>
{
let this = self.clone();
let f = async move {
let mut guard = this.inner.lock().await;
guard.do_state_change(new_state);
return Ok(ReturnToken::new(this.inner.clone()))
};
Box::pin(f)
}
pub fn create_token(&self) -> ReturnToken { pub fn create_token(&self) -> ReturnToken {
ReturnToken::new(self.inner.clone()) ReturnToken::new(self.inner.clone())
} }
@ -161,6 +173,10 @@ impl Machine {
let guard = self.inner.try_lock().unwrap(); let guard = self.inner.try_lock().unwrap();
guard.signal() guard.signal()
} }
pub fn get_inner(&self) -> Arc<Mutex<Inner>> {
self.inner.clone()
}
} }
impl Deref for Machine { impl Deref for Machine {
@ -182,9 +198,6 @@ pub struct Inner {
/// Globally unique machine readable identifier /// Globally unique machine readable identifier
pub id: MachineIdentifier, pub id: MachineIdentifier,
/// Descriptor of the machine
pub desc: MachineDescription,
/// The state of the machine as bffh thinks the machine *should* be in. /// The state of the machine as bffh thinks the machine *should* be in.
/// ///
/// This is a Signal generator. Subscribers to this signal will be notified of changes. In the /// This is a Signal generator. Subscribers to this signal will be notified of changes. In the
@ -195,13 +208,11 @@ pub struct Inner {
impl Inner { impl Inner {
pub fn new ( id: MachineIdentifier pub fn new ( id: MachineIdentifier
, desc: MachineDescription
, state: MachineState , state: MachineState
) -> Inner ) -> Inner
{ {
Inner { Inner {
id: id, id,
desc: desc,
state: Mutable::new(state), state: Mutable::new(state),
reset: None, reset: None,
} }