Implement more API

This commit is contained in:
Nadja Reitzenstein 2022-03-12 17:31:53 +01:00
parent ee57c2b275
commit 87af5fde94
15 changed files with 341 additions and 52 deletions

4
Cargo.lock generated
View File

@ -1885,9 +1885,7 @@ dependencies = [
[[package]] [[package]]
name = "rsasl" name = "rsasl"
version = "2.0.0-preview2" version = "2.0.0-preview3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2adcc7fb89ad42cf35d527905a11232c02fa030f7b6983b8c9880c385da2ae8e"
dependencies = [ dependencies = [
"base64", "base64",
"digest 0.10.3", "digest 0.10.3",

View File

@ -69,7 +69,8 @@ capnp = "0.14"
capnp-rpc = "0.14.1" capnp-rpc = "0.14.1"
# API Authentication # API Authentication
rsasl = "2.0.0-preview2" #rsasl = "2.0.0-preview3"
rsasl = { path = "../../rsasl" }
futures-signals = "0.3.22" futures-signals = "0.3.22"
async-oneshot = "0.5" async-oneshot = "0.5"

View File

@ -16,16 +16,21 @@ impl Inner {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct AuthenticationHandler { pub struct AuthenticationHandle {
inner: Arc<Inner>, inner: Arc<Inner>,
} }
impl AuthenticationHandler { impl AuthenticationHandle {
pub fn new(rsasl: SASL) -> Self { pub fn new() -> Self {
let rsasl = SASL::new();
Self { inner: Arc::new(Inner::new(rsasl)) } Self { inner: Arc::new(Inner::new(rsasl)) }
} }
pub fn start(&self, mechanism: &Mechname) -> Result<Session, SASLError> { pub fn start(&self, mechanism: &Mechname) -> anyhow::Result<Session> {
self.inner.rsasl.server_start(mechanism) Ok(self.inner.rsasl.server_start(mechanism)?)
}
pub fn list_available_mechs(&self) -> impl IntoIterator<Item=&Mechname> {
self.inner.rsasl.server_mech_list().into_iter().map(|m| m.mechanism)
} }
} }

View File

@ -1,3 +1,37 @@
use std::sync::Arc;
use crate::authorization::permissions::Permission;
use crate::authorization::roles::Role;
use crate::users::User;
pub mod permissions; pub mod permissions;
pub mod roles; pub mod roles;
struct Inner {
}
impl Inner {
pub fn new() -> Self {
Self {}
}
}
#[derive(Clone)]
pub struct AuthorizationHandle {
inner: Arc<Inner>,
}
impl AuthorizationHandle {
pub fn new() -> Self {
Self {
inner: Arc::new(Inner::new())
}
}
pub fn lookup_user(&self, uid: impl AsRef<str>) -> Option<User> {
unimplemented!()
}
pub fn is_permitted<'a>(&self, roles: impl IntoIterator<Item=&'a Role>, perm: impl AsRef<Permission>) -> bool {
unimplemented!()
}
}

View File

@ -1,29 +1,97 @@
use std::io::Cursor;
use capnp::capability::Promise; use capnp::capability::Promise;
use capnp::Error; use capnp::Error;
use capnp_rpc::pry; use capnp_rpc::pry;
use rsasl::session::{Session, Step}; use rsasl::property::AuthId;
use rsasl::session::{Session, Step, StepResult};
use std::io::Cursor;
use crate::authorization::AuthorizationHandle;
use crate::capnp::session::APISession;
use crate::session::SessionManager;
use api::authenticationsystem_capnp::authentication::{ use api::authenticationsystem_capnp::authentication::{
Server as AuthenticationSystem, AbortParams, AbortResults, Server as AuthenticationSystem, StepParams, StepResults,
StepParams, StepResults,
AbortParams, AbortResults,
}; };
use api::authenticationsystem_capnp::{response, response::Error as ErrorCode};
pub struct Authentication { pub struct Authentication {
state: State, state: State,
} }
impl Authentication {
pub fn new(session: Session, sessionmanager: SessionManager) -> Self {
Self {
state: State::Running(session, sessionmanager),
}
}
pub fn invalid_mechanism() -> Self {
Self {
state: State::InvalidMechanism,
}
}
fn build_error(&self, response: response::Builder) {
if let State::Running(_, _) = self.state {
return;
}
let mut builder = response.init_failed();
match self.state {
State::InvalidMechanism => builder.set_code(ErrorCode::BadMechanism),
State::Finished => builder.set_code(ErrorCode::Aborted),
State::Aborted => builder.set_code(ErrorCode::Aborted),
_ => unreachable!(),
}
}
}
enum State { enum State {
InvalidMechanism, InvalidMechanism,
Finished, Finished,
Aborted, Aborted,
Running(Session) Running(Session, SessionManager),
} }
impl AuthenticationSystem for Authentication { impl AuthenticationSystem for Authentication {
fn step(&mut self, params: StepParams, mut results: StepResults) -> Promise<(), Error> { fn step(&mut self, params: StepParams, mut results: StepResults) -> Promise<(), Error> {
unimplemented!(); let mut builder = results.get();
if let State::Running(mut session, manager) =
std::mem::replace(&mut self.state, State::Aborted)
{
let data: &[u8] = pry!(pry!(params.get()).get_data());
let mut out = Cursor::new(Vec::new());
match session.step(Some(data), &mut out) {
Ok(Step::Done(data)) => {
self.state = State::Finished;
let uid = pry!(session.get_property::<AuthId>().ok_or(capnp::Error::failed(
"Authentication didn't provide an authid as required".to_string()
)));
let session = pry!(manager.open(uid.as_ref()).ok_or(capnp::Error::failed(
"Failed to lookup the given user".to_string()
)));
let mut builder = builder.init_successful();
if data.is_some() {
builder.set_additional_data(out.into_inner().as_slice());
}
APISession::build(session, builder)
}
Ok(Step::NeedsMore(_)) => {
self.state = State::Running(session, manager);
builder.set_challenge(out.into_inner().as_slice());
}
Err(_) => {
self.state = State::Aborted;
self.build_error(builder);
}
}
} else {
self.build_error(builder);
}
Promise::ok(())
} }
fn abort(&mut self, _: AbortParams, _: AbortResults) -> Promise<(), Error> { fn abort(&mut self, _: AbortParams, _: AbortResults) -> Promise<(), Error> {

View File

@ -1,15 +1,88 @@
use api::connection_capnp::bootstrap::Server as Bootstrap;
pub use api::connection_capnp::bootstrap::Client; pub use api::connection_capnp::bootstrap::Client;
use api::connection_capnp::bootstrap;
use capnp::capability::Promise;
use capnp_rpc::pry;
use rsasl::mechname::Mechname;
use crate::authentication::AuthenticationHandle;
use crate::capnp::authenticationsystem::Authentication;
use crate::session::SessionManager;
#[derive(Debug)]
/// Cap'n Proto API Handler /// Cap'n Proto API Handler
pub struct BootCap; pub struct BootCap {
authentication: AuthenticationHandle,
sessionmanager: SessionManager,
}
impl BootCap { impl BootCap {
pub fn new() -> Self { pub fn new(authentication: AuthenticationHandle, sessionmanager: SessionManager) -> Self {
Self Self {
authentication,
sessionmanager,
}
} }
} }
impl Bootstrap for BootCap { impl bootstrap::Server for BootCap {
fn get_a_p_i_version(
&mut self,
_: bootstrap::GetAPIVersionParams,
_: bootstrap::GetAPIVersionResults,
) -> Promise<(), ::capnp::Error> {
Promise::ok(())
}
fn get_server_release(
&mut self,
_: bootstrap::GetServerReleaseParams,
mut result: bootstrap::GetServerReleaseResults,
) -> Promise<(), ::capnp::Error> {
let mut builder = result.get();
builder.set_name("bffhd");
builder.set_release(crate::RELEASE_STRING);
Promise::ok(())
}
fn mechanisms(
&mut self,
_: bootstrap::MechanismsParams,
mut result: bootstrap::MechanismsResults,
) -> Promise<(), ::capnp::Error> {
let mut builder = result.get();
let mechs: Vec<_> = self.authentication.list_available_mechs()
.into_iter()
.map(|m| m.as_str())
.collect();
let mut mechbuilder = builder.init_mechs(mechs.len() as u32);
for (i,m) in mechs.iter().enumerate() {
mechbuilder.set(i as u32, m);
}
Promise::ok(())
}
fn create_session(
&mut self,
params: bootstrap::CreateSessionParams,
mut result: bootstrap::CreateSessionResults,
) -> Promise<(), ::capnp::Error> {
let params = pry!(params.get());
let mechanism: &str = pry!(params.get_mechanism());
let mechname = Mechname::new(mechanism.as_bytes());
let auth = if let Ok(mechname) = mechname {
if let Ok(session) = self.authentication.start(mechname) {
Authentication::new(session, self.sessionmanager.clone())
} else {
Authentication::invalid_mechanism()
}
} else {
Authentication::invalid_mechanism()
};
let mut builder = result.get();
builder.set_authentication(capnp_rpc::new_client(auth));
Promise::ok(())
}
} }

View File

@ -6,8 +6,11 @@ use api::machine_capnp::machine::{
manage, manage::Server as ManageServer, manage, manage::Server as ManageServer,
use_, use_::Server as UseServer, use_, use_::Server as UseServer,
}; };
use crate::session::SessionHandle;
pub struct Machine; pub struct Machine {
session: SessionHandle,
}
impl InfoServer for Machine { impl InfoServer for Machine {
fn get_property_list( fn get_property_list(

View File

@ -1,19 +1,55 @@
use api::machinesystem_capnp::machine_system::Server as MachineSystem; use crate::authorization::AuthorizationHandle;
use crate::session::SessionHandle;
use api::machinesystem_capnp::machine_system::{
info, InfoParams, InfoResults, Server as MachineSystem,
};
use capnp::capability::Promise;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Machines { pub struct Machines {
session: SessionHandle,
} }
impl Machines { impl Machines {
pub fn new() -> Self { pub fn new(session: SessionHandle) -> Self {
Self { Self { session }
} }
} }
}
impl MachineSystem for Machines { impl MachineSystem for Machines {
fn info(&mut self, _: InfoParams, _: InfoResults) -> Promise<(), ::capnp::Error> {
Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(),
))
}
}
impl info::Server for Machines {
fn get_machine_list(
&mut self,
_: info::GetMachineListParams,
_: info::GetMachineListResults,
) -> Promise<(), ::capnp::Error> {
Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(),
))
}
fn get_machine(
&mut self,
_: info::GetMachineParams,
_: info::GetMachineResults,
) -> Promise<(), ::capnp::Error> {
Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(),
))
}
fn get_machine_u_r_n(
&mut self,
_: info::GetMachineURNParams,
_: info::GetMachineURNResults,
) -> Promise<(), ::capnp::Error> {
Promise::err(::capnp::Error::unimplemented(
"method not implemented".to_string(),
))
}
} }

View File

@ -17,8 +17,11 @@ use std::future::Future;
use std::io; use std::io;
use std::io::BufReader; use std::io::BufReader;
use std::sync::Arc; use std::sync::Arc;
use crate::authentication::AuthenticationHandle;
use crate::authorization::AuthorizationHandle;
use crate::error::Result; use crate::error::Result;
use crate::session::SessionManager;
mod authenticationsystem; mod authenticationsystem;
mod connection; mod connection;
@ -33,6 +36,8 @@ pub struct APIServer {
executor: Executor<'static>, executor: Executor<'static>,
sockets: Vec<TcpListener>, sockets: Vec<TcpListener>,
acceptor: TlsAcceptor, acceptor: TlsAcceptor,
sessionmanager: SessionManager,
authentication: AuthenticationHandle,
} }
impl APIServer { impl APIServer {
@ -40,11 +45,15 @@ impl APIServer {
executor: Executor<'static>, executor: Executor<'static>,
sockets: Vec<TcpListener>, sockets: Vec<TcpListener>,
acceptor: TlsAcceptor, acceptor: TlsAcceptor,
sessionmanager: SessionManager,
authentication: AuthenticationHandle,
) -> Self { ) -> Self {
Self { Self {
executor, executor,
sockets, sockets,
acceptor, acceptor,
sessionmanager,
authentication,
} }
} }
@ -52,6 +61,8 @@ impl APIServer {
executor: Executor<'static>, executor: Executor<'static>,
listens: impl IntoIterator<Item = &Listen>, listens: impl IntoIterator<Item = &Listen>,
acceptor: TlsAcceptor, acceptor: TlsAcceptor,
sessionmanager: SessionManager,
authentication: AuthenticationHandle,
) -> anyhow::Result<Self> { ) -> anyhow::Result<Self> {
let span = tracing::info_span!("binding API listen sockets"); let span = tracing::info_span!("binding API listen sockets");
let _guard = span.enter(); let _guard = span.enter();
@ -100,10 +111,10 @@ impl APIServer {
tracing::warn!("No usable listen addresses configured for the API server!"); tracing::warn!("No usable listen addresses configured for the API server!");
} }
Ok(Self::new(executor, sockets, acceptor)) Ok(Self::new(executor, sockets, acceptor, sessionmanager, authentication))
} }
pub async fn handle_until(&mut self, stop: impl Future) { pub async fn handle_until(self, stop: impl Future) {
stream::select_all( stream::select_all(
self.sockets self.sockets
.iter() .iter()
@ -132,7 +143,8 @@ impl APIServer {
}; };
let (rx, tx) = futures_lite::io::split(stream); let (rx, tx) = futures_lite::io::split(stream);
let vat = VatNetwork::new(rx, tx, Side::Server, Default::default()); let vat = VatNetwork::new(rx, tx, Side::Server, Default::default());
let bootstrap: connection::Client = capnp_rpc::new_client(connection::BootCap::new());
let bootstrap: connection::Client = capnp_rpc::new_client(connection::BootCap::new(self.authentication.clone(), self.sessionmanager.clone()));
if let Err(e) = RpcSystem::new(Box::new(vat), Some(bootstrap.client)).await { if let Err(e) = RpcSystem::new(Box::new(vat), Some(bootstrap.client)).await {
tracing::error!("Error during RPC handling: {}", e); tracing::error!("Error during RPC handling: {}", e);

View File

@ -1,7 +1,15 @@
use api::permissionsystem_capnp::permission_system::Server as PermissionSystem; use api::permissionsystem_capnp::permission_system::Server as PermissionSystem;
use crate::authorization::AuthorizationHandle;
use crate::session::SessionHandle;
pub struct Permissions; pub struct Permissions;
impl Permissions {
pub fn new(session: SessionHandle) -> Self {
Self
}
}
impl PermissionSystem for Permissions { impl PermissionSystem for Permissions {
} }

View File

@ -1,17 +1,24 @@
use api::authenticationsystem_capnp::response::successful::Builder;
use capnp::capability::Promise;
use crate::authorization::AuthorizationHandle;
use crate::capnp::machinesystem::Machines; use crate::capnp::machinesystem::Machines;
use crate::capnp::permissionsystem::Permissions;
use crate::capnp::user_system::Users; use crate::capnp::user_system::Users;
use crate::session::{SessionHandle, SessionManager};
use crate::users::User;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Session { pub struct APISession;
resources: Machines,
users: Users, impl APISession {
pub fn new() -> Self {
Self
} }
impl Session { pub fn build(session: SessionHandle, mut builder: Builder) {
pub fn new() -> Self { let mut builder = builder.init_session();
Session { builder.set_machine_system(capnp_rpc::new_client(Machines::new(session.clone())));
resources: Machines::new(), builder.set_user_system(capnp_rpc::new_client(Users::new(session.clone())));
users: Users::new(), builder.set_permission_system(capnp_rpc::new_client(Permissions::new(session)));
}
} }
} }

View File

@ -4,8 +4,11 @@ use api::user_capnp::user::{
manage, manage,
admin, admin,
}; };
use crate::session::SessionHandle;
struct User; struct User {
session: SessionHandle,
}
impl info::Server for User { impl info::Server for User {

View File

@ -7,16 +7,18 @@ use api::usersystem_capnp::user_system::{
info, info::Server as InfoServer, info, info::Server as InfoServer,
manage, manage::Server as ManageServer, manage, manage::Server as ManageServer,
}; };
use crate::authorization::AuthorizationHandle;
use crate::session::SessionHandle;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Users { pub struct Users {
session: SessionHandle,
} }
impl Users { impl Users {
pub fn new() -> Self { pub fn new(session: SessionHandle) -> Self {
Self { Self {
session,
} }
} }
} }

View File

@ -37,6 +37,7 @@ mod tls;
mod keylog; mod keylog;
mod logging; mod logging;
mod audit; mod audit;
mod session;
use std::fs::File; use std::fs::File;
use std::io::BufReader; use std::io::BufReader;
@ -49,10 +50,14 @@ use rustls::{Certificate, KeyLogFile, PrivateKey, ServerConfig};
use rustls::server::NoClientAuth; use rustls::server::NoClientAuth;
use signal_hook::consts::signal::*; use signal_hook::consts::signal::*;
use executor::pool::Executor; use executor::pool::Executor;
use crate::authentication::AuthenticationHandle;
use crate::capnp::APIServer; use crate::capnp::APIServer;
use crate::config::{Config, TlsListen}; use crate::config::{Config, TlsListen};
use crate::session::SessionManager;
use crate::tls::TlsConfig; use crate::tls::TlsConfig;
pub const RELEASE_STRING: &'static str = env!("BFFHD_RELEASE_STRING");
pub struct Diflouroborane { pub struct Diflouroborane {
executor: Executor<'static>, executor: Executor<'static>,
} }
@ -65,7 +70,6 @@ impl Diflouroborane {
} }
fn log_version_number(&self) { fn log_version_number(&self) {
const RELEASE_STRING: &'static str = env!("BFFHD_RELEASE_STRING");
tracing::info!(version=RELEASE_STRING, "Starting"); tracing::info!(version=RELEASE_STRING, "Starting");
} }
@ -86,7 +90,10 @@ impl Diflouroborane {
let tlsconfig = TlsConfig::new(config.tlskeylog.as_ref(), !config.is_quiet())?; let tlsconfig = TlsConfig::new(config.tlskeylog.as_ref(), !config.is_quiet())?;
let acceptor = tlsconfig.make_tls_acceptor(&config.tlsconfig)?; let acceptor = tlsconfig.make_tls_acceptor(&config.tlsconfig)?;
let mut apiserver = self.executor.run(APIServer::bind(self.executor.clone(), &config.listens, acceptor))?; let sessionmanager = SessionManager::new();
let authentication = AuthenticationHandle::new();
let mut apiserver = self.executor.run(APIServer::bind(self.executor.clone(), &config.listens, acceptor, sessionmanager, authentication))?;
let (mut tx, rx) = async_oneshot::oneshot(); let (mut tx, rx) = async_oneshot::oneshot();

32
bffhd/session/mod.rs Normal file
View File

@ -0,0 +1,32 @@
use std::sync::Arc;
struct Inner {
}
impl Inner {
pub fn new() -> Self {
Self { }
}
}
#[derive(Clone)]
pub struct SessionManager {
inner: Arc<Inner>,
}
impl SessionManager {
pub fn new() -> Self {
Self {
inner: Arc::new(Inner::new()),
}
}
pub fn open(&self, uid: impl AsRef<str>) -> Option<SessionHandle> {
unimplemented!()
}
}
#[derive(Clone, Debug)]
pub struct SessionHandle {
}
impl SessionHandle {
}