Initial commit

This commit is contained in:
Gregor Reitzenstein 2020-02-14 12:20:17 +01:00
commit d5f92e41d1
13 changed files with 237 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
**/*.rs.bk

32
Cargo.toml Normal file
View File

@ -0,0 +1,32 @@
[package]
name = "bffh"
version = "0.1.0"
authors = ["Gregor Reitzenstein <me@dequbed.space>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
# TODO: reduce the feature groups for faster compilation
#tokio = { version = "0.2", features = ["full"] }
async-std = "1.5"
futures = "0.3"
futures-util = "0.3"
futures-signals = "0.3"
slog = "2.5"
slog-term = "2.5"
slog-async = "2.4"
capnp = "0.12"
capnp-rpc = "0.12"
toml = "0.5"
serde = "1"
serde_derive = "1"
casbin = "0.2"
[build-dependencies]
capnpc = "0.12"

0
README.md Normal file
View File

3
build.rs Normal file
View File

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

10
schema/api.capnp Normal file
View File

@ -0,0 +1,10 @@
@0xfd92ce9be2369b8e;
interface BffhAdmin {
getAllSubjects @0 () -> (subjects :List(Subject));
struct Subject {
id @0 :Text;
domain @1 :Text;
}
}

15
src/access.rs Normal file
View File

@ -0,0 +1,15 @@
//! Access control logic
//!
use casbin::prelude::*;
use super::config::Config;
pub async fn init(config: &Config) -> Result<Enforcer, 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(e);
}

48
src/api.rs Normal file
View File

@ -0,0 +1,48 @@
// module needs to be top level for generated functions to be in scope:
// https://github.com/capnproto/capnproto-rust/issues/16
pub(crate) mod api_capnp {
include!(concat!(env!("OUT_DIR"), "/schema/api_capnp.rs"));
}
use std::default::Default;
use async_std::net::TcpStream;
use futures_signals::signal::Mutable;
use casbin::Enforcer;
use casbin::MgmtApi;
pub fn init() {
}
pub async fn process_socket(enforcer: Mutable<Enforcer>, socket: TcpStream) -> Result<(), capnp::Error> {
let api = Api { e: enforcer };
let a = api_capnp::bffh_admin::ToClient::new(api).into_client::<::capnp_rpc::Server>();
let netw = capnp_rpc::twoparty::VatNetwork::new(socket.clone(), socket,
capnp_rpc::rpc_twoparty_capnp::Side::Server, Default::default());
let rpc = capnp_rpc::RpcSystem::new(Box::new(netw), Some(a.clone().client));
rpc.await
}
struct Api {
e: Mutable<Enforcer>,
}
impl api_capnp::bffh_admin::Server for Api {
fn get_all_subjects(&mut self,
_params: api_capnp::bffh_admin::GetAllSubjectsParams,
mut results: api_capnp::bffh_admin::GetAllSubjectsResults)
-> ::capnp::capability::Promise<(), ::capnp::Error>
{
let subjs = self.e.lock_ref().get_all_subjects();
let mut b = results.get()
.init_subjects(subjs.len() as u32);
for (i, s) in subjs.into_iter().enumerate() {
let bldr = b.reborrow();
let mut sub = bldr.get(i as u32);
sub.set_id(&s);
sub.set_domain("");
}
::capnp::capability::Promise::ok(())
}
}

25
src/config.rs Normal file
View File

@ -0,0 +1,25 @@
use std::str::FromStr;
use std::path::PathBuf;
use serde_derive::Deserialize;
use crate::error::Result;
pub fn read() -> Result<Config> {
Ok(Config {
access: Access {
model: PathBuf::from_str("/tmp/model.conf").unwrap(),
policy: PathBuf::from_str("/tmp/policy.csv").unwrap(),
}
})
}
#[derive(Deserialize)]
pub struct Config {
pub(crate) access: Access
}
#[derive(Deserialize)]
pub struct Access {
pub(crate) model: PathBuf,
pub(crate) policy: PathBuf
}

8
src/error.rs Normal file
View File

@ -0,0 +1,8 @@
use std::io;
#[derive(Debug)]
pub enum Error {
IO(io::Error)
}
pub type Result<T> = std::result::Result<T, Error>;

11
src/log.rs Normal file
View File

@ -0,0 +1,11 @@
use slog::{Drain, Logger};
use slog_async;
use slog_term::{TermDecorator, FullFormat};
pub fn init() -> Logger {
let decorator = TermDecorator::new().build();
let drain = FullFormat::new(decorator).build().fuse();
let drain = slog_async::Async::new(drain).build().fuse();
return slog::Logger::root(drain, o!());
}

58
src/main.rs Normal file
View File

@ -0,0 +1,58 @@
#[macro_use]
extern crate slog;
mod access;
mod modules;
mod log;
mod api;
mod config;
mod error;
use api::api_capnp;
use futures::prelude::*;
use futures_signals::signal::Mutable;
use futures::task::LocalSpawn;
fn main() {
let log = log::init();
info!(log, "Starting");
let config = config::read().unwrap();
modules::init(log.new(o!()));
api::init();
let mut exec = futures::executor::LocalPool::new();
let enf = exec.run_until(async {
let e = access::init(&config).await.unwrap();
Mutable::new(e)
});
use std::net::ToSocketAddrs;
let args: Vec<String> = ::std::env::args().collect();
if args.len() != 2 {
println!("usage: {} ADDRESS[:PORT]", args[0]);
return;
}
let addr = args[1].to_socket_addrs().unwrap().next().expect("could not parse address");
let spawner = exec.spawner();
let result: Result<(), Box<dyn std::error::Error>> = exec.run_until(async move {
let listener = async_std::net::TcpListener::bind(&addr).await?;
let mut incoming = listener.incoming();
while let Some(socket) = incoming.next().await {
let socket = socket?;
let rpc_system = api::process_socket(enf.clone(), socket);
spawner.spawn_local_obj(
Box::pin(rpc_system.map_err(|e| println!("error: {:?}", e)).map(|_|())).into()).expect("spawn")
}
Ok(())
});
result.expect("main");
}

16
src/modules.rs Normal file
View File

@ -0,0 +1,16 @@
//! Indpendent Communication modules
//!
//! This is where dynamic modules are implemented later on using libloading / abi_stable_crates et
//! al.
//! Additionally, FFI modules to other languages (Python/Lua/...) make the most sense in here as
//! well.
mod mqtt;
use slog::Logger;
pub fn init(log: Logger) {
info!(log, "Initializing submodules");
mqtt::init(log.new(o!()));
info!(log, "Finished initializing submodules");
}

9
src/modules/mqtt.rs Normal file
View File

@ -0,0 +1,9 @@
//! Mock impl of MQTT as transport.
//!
//! Specific Protocol implementations (Sonoff/Card2Go/...) would be located here
use slog::Logger;
pub fn init(log: Logger) {
info!(log, "MQTT Module initialized.")
}