mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-12-22 19:53:49 +01:00
Now it *actually* compiles again
This commit is contained in:
parent
f6f0f14081
commit
7525fe49f6
@ -28,8 +28,6 @@ capnp-rpc = "0.12"
|
||||
toml = "0.5"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
casbin = "0.2"
|
||||
|
||||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
|
||||
clap = "2.33"
|
||||
|
3
build.rs
3
build.rs
@ -1,3 +1,4 @@
|
||||
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();
|
||||
}
|
||||
|
@ -3,64 +3,20 @@
|
||||
|
||||
use slog::Logger;
|
||||
|
||||
use casbin::prelude::*;
|
||||
|
||||
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 {
|
||||
log: Logger,
|
||||
pdb: Enforcer,
|
||||
}
|
||||
|
||||
impl PermissionsProvider {
|
||||
pub fn new(log: Logger, pdb: Enforcer) -> Self {
|
||||
Self { log, pdb }
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
pub fn new(log: Logger) -> Self {
|
||||
Self { log }
|
||||
}
|
||||
}
|
||||
|
||||
/// This line documents init
|
||||
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?;
|
||||
let adapter = Box::new(FileAdapter::new(config.access.policy.clone()));
|
||||
|
||||
let e = Enforcer::new(model, adapter).await?;
|
||||
|
||||
return Ok(PermissionsProvider::new(log, e));
|
||||
return Ok(PermissionsProvider::new(log));
|
||||
}
|
||||
|
24
src/api.rs
24
src/api.rs
@ -4,32 +4,12 @@ pub mod gen {
|
||||
include!(concat!(env!("OUT_DIR"), "/schema/api_capnp.rs"));
|
||||
}
|
||||
|
||||
use std::default::Default;
|
||||
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 std::rc::Rc;
|
||||
use async_std::sync::{Arc, RwLock};
|
||||
use crate::error::Result;
|
||||
|
||||
use crate::machine::{MachinesProvider, Machines};
|
||||
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> {
|
||||
pub async fn handle_connection(log: Logger, socket: TcpStream) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
160
src/auth.rs
160
src/auth.rs
@ -3,147 +3,43 @@
|
||||
//! Authorization is over in `access.rs`
|
||||
//! 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 rsasl::{SASL, Property, Step, Session, ReturnCode};
|
||||
use rsasl::sys::{Gsasl, Gsasl_session};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::config::Config;
|
||||
|
||||
pub async fn init(log: Logger, config: Config) -> Result<AuthenticationProvider> {
|
||||
let passdb = open_passdb(&config.passdb).unwrap();
|
||||
|
||||
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))
|
||||
pub mod gen {
|
||||
include!(concat!(env!("OUT_DIR"), "/schema/auth_capnp.rs"));
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SASLError {
|
||||
/// Expected UTF-8, got something else
|
||||
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 {}
|
||||
extern "C" fn callback(ctx: *mut Gsasl, sctx: *mut Gsasl_session, prop: Property) -> i32 {
|
||||
let sasl = SASL::from_ptr(ctx);
|
||||
let mut session = Session::from_ptr(sctx);
|
||||
|
||||
type PassDB = HashMap<String, String>;
|
||||
pub fn open_passdb(path: &Path) -> Option<PassDB> {
|
||||
if path.is_file() {
|
||||
let mut fp = File::open(path).unwrap();
|
||||
let mut content = String::new();
|
||||
fp.read_to_string(&mut content).unwrap();
|
||||
let map = toml::from_str(&content).ok()?;
|
||||
return Some(map);
|
||||
} else {
|
||||
let mut map = HashMap::new();
|
||||
map.insert("Testuser".to_string(), "Testpass".to_string());
|
||||
let mut fp = File::create(&path).unwrap();
|
||||
let toml = toml::to_string(&map).unwrap();
|
||||
fp.write_all(&toml.as_bytes()).unwrap();
|
||||
return Some(map);
|
||||
let rc = match prop {
|
||||
_ => { ReturnCode::GSASL_NO_CALLBACK }
|
||||
};
|
||||
|
||||
rc as i32
|
||||
}
|
||||
|
||||
pub struct Auth {
|
||||
ctx: SASL,
|
||||
}
|
||||
|
||||
impl Auth {
|
||||
pub fn new() -> Self {
|
||||
let mut ctx = SASL::new().unwrap();
|
||||
|
||||
ctx.install_callback(Some(callback));
|
||||
|
||||
Self { ctx }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Plain {
|
||||
// FIXME: I don't want to store passwords.
|
||||
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,
|
||||
}
|
||||
}
|
||||
pub async fn init(log: Logger, config: Config) -> Result<Auth> {
|
||||
Ok(Auth::new())
|
||||
}
|
||||
|
31
src/error.rs
31
src/error.rs
@ -1,19 +1,42 @@
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use toml;
|
||||
|
||||
use crate::auth::SASLError;
|
||||
use rsasl::SaslError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
TomlDe(toml::de::Error),
|
||||
TomlSer(toml::ser::Error),
|
||||
SASL(SASLError),
|
||||
SASL(SaslError),
|
||||
IO(io::Error),
|
||||
Boxed(Box<dyn std::error::Error>),
|
||||
}
|
||||
|
||||
impl From<SASLError> for Error {
|
||||
fn from(e: SASLError) -> Error {
|
||||
impl fmt::Display for 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)
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ use toml;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::config::Config;
|
||||
use crate::access::Permissions;
|
||||
|
||||
use std::rc::Rc;
|
||||
use async_std::sync::{Arc, RwLock};
|
||||
@ -90,11 +89,10 @@ impl MachinesProvider {
|
||||
#[derive(Clone)]
|
||||
pub struct Machines {
|
||||
inner: Arc<RwLock<MachinesProvider>>,
|
||||
perm: Rc<Permissions>,
|
||||
}
|
||||
impl Machines {
|
||||
pub fn new(inner: Arc<RwLock<MachinesProvider>>, perm: Rc<Permissions>) -> Self {
|
||||
Self { inner, perm }
|
||||
pub fn new(inner: Arc<RwLock<MachinesProvider>>) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,6 @@ use signal_hook::iterator::Signals;
|
||||
|
||||
use clap::{App, Arg};
|
||||
|
||||
use api::gen as api_capnp;
|
||||
|
||||
use futures::prelude::*;
|
||||
use futures::executor::{LocalPool, ThreadPool};
|
||||
use futures::compat::Stream01CompatExt;
|
||||
@ -40,6 +38,12 @@ use std::sync::Arc;
|
||||
|
||||
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.
|
||||
// In the case of an Err it will be printed using `fmt::Debug`
|
||||
fn main() -> Result<(), Error> {
|
||||
|
Loading…
Reference in New Issue
Block a user