Now it *actually* compiles again

This commit is contained in:
Gregor Reitzenstein 2020-05-04 13:22:14 +02:00
parent f6f0f14081
commit 7525fe49f6
8 changed files with 70 additions and 214 deletions

View File

@ -28,8 +28,6 @@ capnp-rpc = "0.12"
toml = "0.5" toml = "0.5"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
casbin = "0.2"
uuid = { version = "0.8", features = ["serde", "v4"] } uuid = { version = "0.8", features = ["serde", "v4"] }
clap = "2.33" clap = "2.33"

View File

@ -1,3 +1,4 @@
fn main() { fn main() {
::capnpc::CompilerCommand::new().file("schema/api.capnp").run().unwrap() ::capnpc::CompilerCommand::new().file("schema/api.capnp").run().unwrap();
::capnpc::CompilerCommand::new().file("schema/auth.capnp").run().unwrap();
} }

View File

@ -3,64 +3,20 @@
use slog::Logger; use slog::Logger;
use casbin::prelude::*;
use crate::config::Config; use crate::config::Config;
use crate::auth::Authentication;
use crate::error::Result;
use std::rc::Rc;
use async_std::sync::{Arc, RwLock};
use std::ops::Deref;
pub struct PermissionsProvider { pub struct PermissionsProvider {
log: Logger, log: Logger,
pdb: Enforcer,
} }
impl PermissionsProvider { impl PermissionsProvider {
pub fn new(log: Logger, pdb: Enforcer) -> Self { pub fn new(log: Logger) -> Self {
Self { log, pdb } Self { log }
}
pub fn enforce(&self, actor: &str, object: &str, action: &str) -> Result<bool> {
let b = self.pdb.enforce(vec![actor, object, action])?;
if b {
trace!(self.log, "Granted {} on {} for {}", action, object, actor);
} else {
trace!(self.log, "Denied {} on {} for {}", action, object, actor);
}
Ok(b)
}
}
#[derive(Clone)]
pub struct Permissions {
inner: Arc<RwLock<PermissionsProvider>>,
auth: Rc<Authentication>,
}
impl Permissions {
pub fn new(inner: Arc<RwLock<PermissionsProvider>>, auth: Rc<Authentication>) -> Self {
Self { inner, auth }
}
pub async fn enforce(&self, object: &str, action: &str) -> Result<bool> {
if let Some(actor) = self.auth.state.read().await.deref() {
self.inner.read().await.enforce(&actor, object, action)
} else {
Ok(false)
}
} }
} }
/// This line documents init /// This line documents init
pub async fn init(log: Logger, config: &Config) -> std::result::Result<PermissionsProvider, Box<dyn std::error::Error>> { pub async fn init(log: Logger, config: &Config) -> std::result::Result<PermissionsProvider, Box<dyn std::error::Error>> {
let model = Model::from_file(config.access.model.clone()).await?; return Ok(PermissionsProvider::new(log));
let adapter = Box::new(FileAdapter::new(config.access.policy.clone()));
let e = Enforcer::new(model, adapter).await?;
return Ok(PermissionsProvider::new(log, e));
} }

View File

@ -4,32 +4,12 @@ pub mod gen {
include!(concat!(env!("OUT_DIR"), "/schema/api_capnp.rs")); include!(concat!(env!("OUT_DIR"), "/schema/api_capnp.rs"));
} }
use std::default::Default;
use async_std::net::TcpStream; use async_std::net::TcpStream;
use futures::task::Spawn;
use futures::FutureExt;
use futures_signals::signal::Mutable;
use casbin::Enforcer;
use casbin::MgmtApi;
use slog::Logger; use slog::Logger;
use std::rc::Rc; use crate::error::Result;
use async_std::sync::{Arc, RwLock};
use crate::machine::{MachinesProvider, Machines}; pub async fn handle_connection(log: Logger, socket: TcpStream) -> Result<()> {
use crate::auth::{AuthenticationProvider, Authentication};
use crate::access::{PermissionsProvider, Permissions};
use capnp::{Error};
use capnp::capability::Promise;
use capnp_rpc::RpcSystem;
use capnp_rpc::twoparty::VatNetwork;
use capnp_rpc::rpc_twoparty_capnp::Side;
use std::ops::Deref;
pub async fn handle_connection(log: Logger, socket: TcpStream) -> Result<(), Error> {
unimplemented!() unimplemented!()
} }

View File

@ -3,147 +3,43 @@
//! Authorization is over in `access.rs` //! Authorization is over in `access.rs`
//! Authentication using SASL //! Authentication using SASL
use std::collections::HashMap;
use std::fmt;
use std::error::Error;
use std::path::Path;
use std::fs::File;
use std::io::{Read, Write};
use std::ops::Deref;
use async_std::sync::{Arc, RwLock};
use capnp::capability::Promise;
use futures_signals::signal::Mutable;
use casbin::{Enforcer, Model, FileAdapter};
use slog::Logger; use slog::Logger;
use rsasl::{SASL, Property, Step, Session, ReturnCode};
use rsasl::sys::{Gsasl, Gsasl_session};
use crate::error::Result; use crate::error::Result;
use crate::config::Config; use crate::config::Config;
pub async fn init(log: Logger, config: Config) -> Result<AuthenticationProvider> { pub mod gen {
let passdb = open_passdb(&config.passdb).unwrap(); include!(concat!(env!("OUT_DIR"), "/schema/auth_capnp.rs"));
let m = Model::from_file(&config.access.model).await?;
let a = FileAdapter::new(config.access.policy);
let enforcer = Enforcer::new(m, Box::new(a)).await?;
Ok(AuthenticationProvider::new(passdb, enforcer))
} }
#[derive(Debug)] extern "C" fn callback(ctx: *mut Gsasl, sctx: *mut Gsasl_session, prop: Property) -> i32 {
pub enum SASLError { let sasl = SASL::from_ptr(ctx);
/// Expected UTF-8, got something else let mut session = Session::from_ptr(sctx);
UTF8,
/// A bad Challenge was provided
BadChallenge,
/// Enforcer Failure
Enforcer,
}
impl fmt::Display for SASLError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Bad SASL Exchange")
}
}
impl Error for SASLError {}
type PassDB = HashMap<String, String>; let rc = match prop {
pub fn open_passdb(path: &Path) -> Option<PassDB> { _ => { ReturnCode::GSASL_NO_CALLBACK }
if path.is_file() { };
let mut fp = File::open(path).unwrap();
let mut content = String::new(); rc as i32
fp.read_to_string(&mut content).unwrap(); }
let map = toml::from_str(&content).ok()?;
return Some(map); pub struct Auth {
} else { ctx: SASL,
let mut map = HashMap::new(); }
map.insert("Testuser".to_string(), "Testpass".to_string());
let mut fp = File::create(&path).unwrap(); impl Auth {
let toml = toml::to_string(&map).unwrap(); pub fn new() -> Self {
fp.write_all(&toml.as_bytes()).unwrap(); let mut ctx = SASL::new().unwrap();
return Some(map);
ctx.install_callback(Some(callback));
Self { ctx }
} }
} }
pub struct Plain { pub async fn init(log: Logger, config: Config) -> Result<Auth> {
// FIXME: I don't want to store passwords. Ok(Auth::new())
passdb: PassDB,
enforcer: Enforcer,
}
impl Plain {
pub fn step<'a>(&self, data: &'a [u8]) -> Result<(bool, &'a str)> {
let data = std::str::from_utf8(data).map_err(|_| SASLError::UTF8)?;
if let Some((authzid, authcid, passwd)) = split_nul(data) {
// Check if we know about that user
if let Some(pwd) = self.passdb.get(authcid) {
// Check the provided password
// FIXME: At least use hashes
if pwd == passwd {
// authzid is the Identity the user wants to act as.
// If that is unset, shortcut to Success
if authzid == "" || authzid == authcid {
return Ok((true, authcid));
}
if let Ok(b) = self.enforcer.enforce(vec![authcid, authzid, "su"]) {
if b {
return Ok((true, authzid));
} else {
return Ok((false, authzid));
}
} else {
return Err(SASLError::Enforcer.into());
}
}
}
Ok((false, authzid))
} else {
return Err(SASLError::BadChallenge.into())
}
}
}
pub fn split_nul(string: &str) -> Option<(&str, &str, &str)> {
let mut i = string.split(|b| b == '\0');
let a = i.next()?;
let b = i.next()?;
let c = i.next()?;
Some((a,b,c))
}
pub struct AuthenticationProvider {
pub plain: Plain,
}
impl AuthenticationProvider {
pub fn new(passdb: PassDB, enforcer: Enforcer) -> Self {
Self {
plain: Plain { passdb, enforcer }
}
}
pub fn mechs(&self) -> Vec<&'static str> {
vec!["PLAIN"]
}
}
#[derive(Clone)]
pub struct Authentication {
pub state: Arc<RwLock<Option<String>>>,
provider: Arc<RwLock<AuthenticationProvider>>,
}
impl Authentication {
pub fn new(provider: Arc<RwLock<AuthenticationProvider>>) -> Self {
Self {
state: Arc::new(RwLock::new(None)),
provider: provider,
}
}
} }

View File

@ -1,19 +1,42 @@
use std::io; use std::io;
use std::fmt;
use toml; use toml;
use crate::auth::SASLError; use rsasl::SaslError;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
TomlDe(toml::de::Error), TomlDe(toml::de::Error),
TomlSer(toml::ser::Error), TomlSer(toml::ser::Error),
SASL(SASLError), SASL(SaslError),
IO(io::Error), IO(io::Error),
Boxed(Box<dyn std::error::Error>), Boxed(Box<dyn std::error::Error>),
} }
impl From<SASLError> for Error { impl fmt::Display for Error {
fn from(e: SASLError) -> Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::TomlDe(e) => {
write!(f, "TOML Decoding error: {}", e)
},
Error::TomlSer(e) => {
write!(f, "TOML Serialization error: {}", e)
},
Error::SASL(e) => {
write!(f, "SASL Error: {}", e)
},
Error::IO(e) => {
write!(f, "IO Error: {}", e)
},
Error::Boxed(e) => {
write!(f, "{}", e)
}
}
}
}
impl From<SaslError> for Error {
fn from(e: SaslError) -> Error {
Error::SASL(e) Error::SASL(e)
} }
} }

View File

@ -10,7 +10,6 @@ use toml;
use crate::error::Result; use crate::error::Result;
use crate::config::Config; use crate::config::Config;
use crate::access::Permissions;
use std::rc::Rc; use std::rc::Rc;
use async_std::sync::{Arc, RwLock}; use async_std::sync::{Arc, RwLock};
@ -90,11 +89,10 @@ impl MachinesProvider {
#[derive(Clone)] #[derive(Clone)]
pub struct Machines { pub struct Machines {
inner: Arc<RwLock<MachinesProvider>>, inner: Arc<RwLock<MachinesProvider>>,
perm: Rc<Permissions>,
} }
impl Machines { impl Machines {
pub fn new(inner: Arc<RwLock<MachinesProvider>>, perm: Rc<Permissions>) -> Self { pub fn new(inner: Arc<RwLock<MachinesProvider>>) -> Self {
Self { inner, perm } Self { inner }
} }
} }

View File

@ -17,8 +17,6 @@ use signal_hook::iterator::Signals;
use clap::{App, Arg}; use clap::{App, Arg};
use api::gen as api_capnp;
use futures::prelude::*; use futures::prelude::*;
use futures::executor::{LocalPool, ThreadPool}; use futures::executor::{LocalPool, ThreadPool};
use futures::compat::Stream01CompatExt; use futures::compat::Stream01CompatExt;
@ -40,6 +38,12 @@ use std::sync::Arc;
use error::Error; use error::Error;
// Re-Export generated capnp code.
// This is necessary because the Rust generator expects types to be found in the
// `crate::<file>_capnp` hierarchy.
use api::gen as api_capnp;
use auth::gen as auth_capnp;
// Returning a `Result` from `main` allows us to use the `?` shorthand. // Returning a `Result` from `main` allows us to use the `?` shorthand.
// In the case of an Err it will be printed using `fmt::Debug` // In the case of an Err it will be printed using `fmt::Debug`
fn main() -> Result<(), Error> { fn main() -> Result<(), Error> {