mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-22 14:57:56 +01:00
Start the user API
This commit is contained in:
parent
006ae0af68
commit
4e3bb44040
69
src/api.rs
69
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 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {}
|
||||||
|
@ -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> {
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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)
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user