mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-12-22 03:33:48 +01:00
Compare commits
10 Commits
beb6eb01a2
...
cd644ea853
Author | SHA1 | Date | |
---|---|---|---|
|
cd644ea853 | ||
57a0436ca1 | |||
|
8b15acf983 | ||
|
40ba114e61 | ||
|
c2c34ede67 | ||
|
2b0fe0e868 | ||
|
fbfb76c34e | ||
|
971dee36fd | ||
|
41983e6039 | ||
|
a62a5678dc |
@ -56,7 +56,7 @@ But before you open an issue in this repo for a feature request, please first ch
|
|||||||
|
|
||||||
## Contributing Code
|
## Contributing Code
|
||||||
|
|
||||||
To help develop Diflouroborane you will need a Rust toolchain. I heavily recommend installing
|
To help develop Difluoroborane you will need a Rust toolchain. I heavily recommend installing
|
||||||
[rustup](https://rustup.rs) even if your distribution provides a recent enough rustc, simply because
|
[rustup](https://rustup.rs) even if your distribution provides a recent enough rustc, simply because
|
||||||
it allows to easily switch compilers between several versions of both stable and nightly. It also
|
it allows to easily switch compilers between several versions of both stable and nightly. It also
|
||||||
allows you to download the respective stdlib crate, giving you the option of an offline reference.
|
allows you to download the respective stdlib crate, giving you the option of an offline reference.
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
Currently there are no distribution packages available.
|
Currently there are no distribution packages available.
|
||||||
However installation is reasonably straight-forward, since Diflouroborane compiles into a single
|
However installation is reasonably straight-forward, since Difluoroborane compiles into a single
|
||||||
mostly static binary with few dependencies.
|
mostly static binary with few dependencies.
|
||||||
|
|
||||||
At the moment only Linux is supported. If you managed to compile Diflouroborane please open an issue
|
At the moment only Linux is supported. If you managed to compile Difluoroborane please open an issue
|
||||||
outlining your steps or add a merge request expanding this part. Thanks!
|
outlining your steps or add a merge request expanding this part. Thanks!
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
@ -12,7 +12,7 @@ outlining your steps or add a merge request expanding this part. Thanks!
|
|||||||
General requirements; scroll down for distribution-specific instructions
|
General requirements; scroll down for distribution-specific instructions
|
||||||
|
|
||||||
- GNU SASL (libgsasl).
|
- GNU SASL (libgsasl).
|
||||||
* If you want to compile Diflouroborane from source you will potentially also need development
|
* If you want to compile Difluoroborane from source you will potentially also need development
|
||||||
headers
|
headers
|
||||||
- capnproto
|
- capnproto
|
||||||
- rustc stable / nightly >= 1.48
|
- rustc stable / nightly >= 1.48
|
||||||
@ -26,7 +26,7 @@ $ pacman -S gsasl rust capnproto
|
|||||||
|
|
||||||
## Compiling from source
|
## Compiling from source
|
||||||
|
|
||||||
Diflouroborane uses Cargo, so compilation boils down to:
|
Difluoroborane uses Cargo, so compilation boils down to:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ cargo build --release
|
$ cargo build --release
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# FabAccess Diflouroborane
|
# FabAccess Difluoroborane
|
||||||
|
|
||||||
Diflouroborane (shorter: BFFH, the chemical formula for Diflouroborane) is the server part of
|
Difluoroborane (shorter: BFFH, the chemical formula for Difluoroborane) is the server part of
|
||||||
FabAccess.
|
FabAccess.
|
||||||
It provides a server-side implementation of the [FabAccess API](https://gitlab.com/fabinfra/fabaccess/fabaccess-api).
|
It provides a server-side implementation of the [FabAccess API](https://gitlab.com/fabinfra/fabaccess/fabaccess-api).
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ use std::future::Future;
|
|||||||
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use miette::{Diagnostic, IntoDiagnostic};
|
use miette::Diagnostic;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -2,7 +2,6 @@ use desfire::desfire::desfire::MAX_BYTES_PER_TRANSACTION;
|
|||||||
use desfire::desfire::Desfire;
|
use desfire::desfire::Desfire;
|
||||||
use desfire::error::Error as DesfireError;
|
use desfire::error::Error as DesfireError;
|
||||||
use desfire::iso7816_4::apduresponse::APDUResponse;
|
use desfire::iso7816_4::apduresponse::APDUResponse;
|
||||||
use rsasl::callback::SessionData;
|
|
||||||
use rsasl::mechanism::{
|
use rsasl::mechanism::{
|
||||||
Authentication, Demand, DemandReply, MechanismData, MechanismError, MechanismErrorKind,
|
Authentication, Demand, DemandReply, MechanismData, MechanismError, MechanismErrorKind,
|
||||||
Provider, State, ThisProvider,
|
Provider, State, ThisProvider,
|
||||||
@ -13,7 +12,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::authentication::fabfire::FabFireCardKey;
|
use crate::authentication::fabfire::FabFireCardKey;
|
||||||
|
|
||||||
@ -104,6 +102,7 @@ struct AuthInfo {
|
|||||||
iv: Vec<u8>,
|
iv: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
#[serde(tag = "Cmd")]
|
#[serde(tag = "Cmd")]
|
||||||
enum CardCommand {
|
enum CardCommand {
|
||||||
|
@ -2,7 +2,6 @@ use desfire::desfire::desfire::MAX_BYTES_PER_TRANSACTION;
|
|||||||
use desfire::desfire::Desfire;
|
use desfire::desfire::Desfire;
|
||||||
use desfire::error::Error as DesfireError;
|
use desfire::error::Error as DesfireError;
|
||||||
use desfire::iso7816_4::apduresponse::APDUResponse;
|
use desfire::iso7816_4::apduresponse::APDUResponse;
|
||||||
use rsasl::callback::SessionData;
|
|
||||||
use rsasl::mechanism::{
|
use rsasl::mechanism::{
|
||||||
Authentication, Demand, DemandReply, MechanismData, MechanismError, MechanismErrorKind,
|
Authentication, Demand, DemandReply, MechanismData, MechanismError, MechanismErrorKind,
|
||||||
Provider, State, ThisProvider,
|
Provider, State, ThisProvider,
|
||||||
@ -13,7 +12,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::authentication::fabfire::FabFireCardKey;
|
use crate::authentication::fabfire::FabFireCardKey;
|
||||||
use crate::CONFIG;
|
use crate::CONFIG;
|
||||||
|
@ -26,7 +26,7 @@ impl Callback {
|
|||||||
impl SessionCallback for Callback {
|
impl SessionCallback for Callback {
|
||||||
fn callback(
|
fn callback(
|
||||||
&self,
|
&self,
|
||||||
session_data: &SessionData,
|
_session_data: &SessionData,
|
||||||
context: &Context,
|
context: &Context,
|
||||||
request: &mut Request,
|
request: &mut Request,
|
||||||
) -> Result<(), SessionError> {
|
) -> Result<(), SessionError> {
|
||||||
|
@ -4,10 +4,8 @@ use capnp_rpc::pry;
|
|||||||
use rsasl::mechname::Mechname;
|
use rsasl::mechname::Mechname;
|
||||||
use rsasl::prelude::State as SaslState;
|
use rsasl::prelude::State as SaslState;
|
||||||
use rsasl::prelude::{MessageSent, Session};
|
use rsasl::prelude::{MessageSent, Session};
|
||||||
use rsasl::property::AuthId;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::{Formatter, Write};
|
use std::fmt::{Formatter, Write};
|
||||||
use std::io::Cursor;
|
|
||||||
use tracing::Span;
|
use tracing::Span;
|
||||||
|
|
||||||
use crate::authentication::V;
|
use crate::authentication::V;
|
||||||
@ -115,7 +113,7 @@ impl AuthenticationSystem for Authentication {
|
|||||||
f.write_char(')')
|
f.write_char(')')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut response;
|
let response;
|
||||||
|
|
||||||
let mut builder = results.get();
|
let mut builder = results.get();
|
||||||
if let State::Running(mut session, manager) =
|
if let State::Running(mut session, manager) =
|
||||||
|
@ -211,7 +211,6 @@ impl ManageServer for Machine {
|
|||||||
mut result: manage::GetMachineInfoExtendedResults,
|
mut result: manage::GetMachineInfoExtendedResults,
|
||||||
) -> Promise<(), ::capnp::Error> {
|
) -> Promise<(), ::capnp::Error> {
|
||||||
let mut builder = result.get();
|
let mut builder = result.get();
|
||||||
let user = User::new_self(self.session.clone());
|
|
||||||
User::build_optional(
|
User::build_optional(
|
||||||
&self.session,
|
&self.session,
|
||||||
self.resource.get_current_user(),
|
self.resource.get_current_user(),
|
||||||
|
@ -5,11 +5,11 @@ use async_net::TcpListener;
|
|||||||
use capnp_rpc::rpc_twoparty_capnp::Side;
|
use capnp_rpc::rpc_twoparty_capnp::Side;
|
||||||
use capnp_rpc::twoparty::VatNetwork;
|
use capnp_rpc::twoparty::VatNetwork;
|
||||||
use capnp_rpc::RpcSystem;
|
use capnp_rpc::RpcSystem;
|
||||||
use executor::prelude::{Executor, GroupId, SupervisionRegistry};
|
use executor::prelude::{Executor, SupervisionRegistry};
|
||||||
use futures_rustls::server::TlsStream;
|
use futures_rustls::server::TlsStream;
|
||||||
use futures_rustls::TlsAcceptor;
|
use futures_rustls::TlsAcceptor;
|
||||||
use futures_util::stream::FuturesUnordered;
|
use futures_util::stream::FuturesUnordered;
|
||||||
use futures_util::{stream, AsyncRead, AsyncWrite, FutureExt, StreamExt};
|
use futures_util::{stream, AsyncRead, AsyncWrite, StreamExt};
|
||||||
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use crate::authorization::roles::Role;
|
|
||||||
use crate::Roles;
|
use crate::Roles;
|
||||||
use api::permissionsystem_capnp::permission_system::info::{
|
use api::permissionsystem_capnp::permission_system::info::{
|
||||||
GetRoleListParams, GetRoleListResults, Server as PermissionSystem,
|
GetRoleListParams, GetRoleListResults, Server as PermissionSystem,
|
||||||
@ -37,7 +36,7 @@ impl PermissionSystem for Permissions {
|
|||||||
|
|
||||||
tracing::trace!("method call");
|
tracing::trace!("method call");
|
||||||
let roles = self.roles.list().collect::<Vec<&String>>();
|
let roles = self.roles.list().collect::<Vec<&String>>();
|
||||||
let mut builder = results.get();
|
let builder = results.get();
|
||||||
let mut b = builder.init_role_list(roles.len() as u32);
|
let mut b = builder.init_role_list(roles.len() as u32);
|
||||||
for (i, role) in roles.into_iter().enumerate() {
|
for (i, role) in roles.into_iter().enumerate() {
|
||||||
let mut role_builder = b.reborrow().get(i as u32);
|
let mut role_builder = b.reborrow().get(i as u32);
|
||||||
|
@ -109,7 +109,7 @@ impl manage::Server for User {
|
|||||||
if let Some(mut user) = self.session.users.get_user(uid) {
|
if let Some(mut user) = self.session.users.get_user(uid) {
|
||||||
if let Ok(true) = user.check_password(old_pw.as_bytes()) {
|
if let Ok(true) = user.check_password(old_pw.as_bytes()) {
|
||||||
user.set_pw(new_pw.as_bytes());
|
user.set_pw(new_pw.as_bytes());
|
||||||
self.session.users.put_user(uid, &user);
|
pry!(self.session.users.put_user(uid, &user));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
@ -143,9 +143,9 @@ impl admin::Server for User {
|
|||||||
// Only update if needed
|
// Only update if needed
|
||||||
if !target.userdata.roles.iter().any(|r| r.as_str() == rolename) {
|
if !target.userdata.roles.iter().any(|r| r.as_str() == rolename) {
|
||||||
target.userdata.roles.push(rolename.to_string());
|
target.userdata.roles.push(rolename.to_string());
|
||||||
self.session
|
pry!(self.session
|
||||||
.users
|
.users
|
||||||
.put_user(self.user.get_username(), &target);
|
.put_user(self.user.get_username(), &target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,9 +168,9 @@ impl admin::Server for User {
|
|||||||
// Only update if needed
|
// Only update if needed
|
||||||
if target.userdata.roles.iter().any(|r| r.as_str() == rolename) {
|
if target.userdata.roles.iter().any(|r| r.as_str() == rolename) {
|
||||||
target.userdata.roles.retain(|r| r.as_str() != rolename);
|
target.userdata.roles.retain(|r| r.as_str() != rolename);
|
||||||
self.session
|
pry!(self.session
|
||||||
.users
|
.users
|
||||||
.put_user(self.user.get_username(), &target);
|
.put_user(self.user.get_username(), &target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ impl admin::Server for User {
|
|||||||
let uid = self.user.get_username();
|
let uid = self.user.get_username();
|
||||||
if let Some(mut user) = self.session.users.get_user(uid) {
|
if let Some(mut user) = self.session.users.get_user(uid) {
|
||||||
user.set_pw(new_pw.as_bytes());
|
user.set_pw(new_pw.as_bytes());
|
||||||
self.session.users.put_user(uid, &user);
|
pry!(self.session.users.put_user(uid, &user));
|
||||||
}
|
}
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
@ -221,7 +221,7 @@ impl card_d_e_s_fire_e_v2::Server for User {
|
|||||||
Vec::new()
|
Vec::new()
|
||||||
});
|
});
|
||||||
if !tk.is_empty() {
|
if !tk.is_empty() {
|
||||||
let mut b = results.get();
|
let b = results.get();
|
||||||
let mut lb = b.init_token_list(1);
|
let mut lb = b.init_token_list(1);
|
||||||
lb.set(0, &tk[..]);
|
lb.set(0, &tk[..]);
|
||||||
}
|
}
|
||||||
@ -299,7 +299,8 @@ impl card_d_e_s_fire_e_v2::Server for User {
|
|||||||
.insert("cardtoken".to_string(), token.to_string());
|
.insert("cardtoken".to_string(), token.to_string());
|
||||||
user.userdata.kv.insert("cardkey".to_string(), card_key);
|
user.userdata.kv.insert("cardkey".to_string(), card_key);
|
||||||
|
|
||||||
self.session.users.put_user(self.user.get_username(), &user);
|
pry!(self.session.users.put_user(self.user.get_username(), &user));
|
||||||
|
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,7 +339,7 @@ impl card_d_e_s_fire_e_v2::Server for User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.session.users.put_user(self.user.get_username(), &user);
|
pry!(self.session.users.put_user(self.user.get_username(), &user));
|
||||||
|
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
@ -84,13 +84,13 @@ impl manage::Server for Users {
|
|||||||
"method call"
|
"method call"
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut builder = result.get();
|
let builder = result.get();
|
||||||
|
|
||||||
if !username.is_empty() && !password.is_empty() {
|
if !username.is_empty() && !password.is_empty() {
|
||||||
if self.session.users.get_user(username).is_none() {
|
if self.session.users.get_user(username).is_none() {
|
||||||
let user = db::User::new_with_plain_pw(username, password);
|
let user = db::User::new_with_plain_pw(username, password);
|
||||||
self.session.users.put_user(username, &user);
|
pry!(self.session.users.put_user(username, &user));
|
||||||
let mut builder = builder.init_successful();
|
let builder = builder.init_successful();
|
||||||
User::fill(&self.session, user, builder);
|
User::fill(&self.session, user, builder);
|
||||||
} else {
|
} else {
|
||||||
let mut builder = builder.init_failed();
|
let mut builder = builder.init_failed();
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::error::Error;
|
use std::fmt::Debug;
|
||||||
use std::fmt::{Debug, Display};
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -12,7 +10,6 @@ use crate::authorization::roles::Role;
|
|||||||
use crate::capnp::{Listen, TlsListen};
|
use crate::capnp::{Listen, TlsListen};
|
||||||
use crate::logging::LogConfig;
|
use crate::logging::LogConfig;
|
||||||
|
|
||||||
use miette::IntoDiagnostic;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -38,13 +38,15 @@ pub fn read(file: impl AsRef<Path>) -> Result<Config, ConfigError> {
|
|||||||
if !path.is_file() {
|
if !path.is_file() {
|
||||||
return Err(ConfigError::NotAFile(path.to_string_lossy().to_string()));
|
return Err(ConfigError::NotAFile(path.to_string_lossy().to_string()));
|
||||||
}
|
}
|
||||||
let mut config = dhall::read_config_file(file)?;
|
let config = dhall::read_config_file(file)?;
|
||||||
for (envvar, value) in std::env::vars() {
|
// TODO: configuration by environment variables?
|
||||||
match envvar.as_str() {
|
// but rather in in a separate function
|
||||||
// Do things like this?
|
// for (envvar, value) in std::env::vars() {
|
||||||
// "BFFH_LOG" => config.logging.filter = Some(value),
|
// match envvar.as_str() {
|
||||||
_ => {}
|
// // Do things like this?
|
||||||
}
|
// // "BFFH_LOG" => config.logging.filter = Some(value),
|
||||||
}
|
// _ => {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
// for converting a database error into a failed promise
|
||||||
|
use capnp;
|
||||||
|
|
||||||
mod raw;
|
mod raw;
|
||||||
|
|
||||||
use miette::{Diagnostic, LabeledSpan, Severity, SourceCode};
|
use miette::{Diagnostic, Severity};
|
||||||
pub use raw::RawDB;
|
pub use raw::RawDB;
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
mod typed;
|
mod typed;
|
||||||
pub use typed::{Adapter, AlignedAdapter, ArchivedValue, DB};
|
pub use typed::{Adapter, AlignedAdapter, ArchivedValue, DB};
|
||||||
@ -79,3 +82,9 @@ impl Diagnostic for Error {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Error> for capnp::Error {
|
||||||
|
fn from(dberr: Error) -> capnp::Error {
|
||||||
|
capnp::Error::failed(format!("database error: {}", dberr.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use super::Result;
|
|
||||||
use lmdb::{DatabaseFlags, Environment, RwTransaction, Transaction, WriteFlags};
|
use lmdb::{DatabaseFlags, Environment, RwTransaction, Transaction, WriteFlags};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use miette::{Diagnostic, LabeledSpan, Severity, SourceCode};
|
use miette::{Diagnostic, Severity};
|
||||||
use std::error;
|
use std::error;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
@ -5,14 +5,11 @@ use super::Initiator;
|
|||||||
use crate::initiators::InitiatorCallbacks;
|
use crate::initiators::InitiatorCallbacks;
|
||||||
use crate::resources::modules::fabaccess::Status;
|
use crate::resources::modules::fabaccess::Status;
|
||||||
use crate::session::SessionHandle;
|
use crate::session::SessionHandle;
|
||||||
use crate::users::UserRef;
|
|
||||||
use async_io::Timer;
|
use async_io::Timer;
|
||||||
use futures_util::future::BoxFuture;
|
use futures_util::future::BoxFuture;
|
||||||
use futures_util::ready;
|
use futures_util::ready;
|
||||||
use lmdb::Stat;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::mem;
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
@ -64,10 +61,7 @@ impl Future for Dummy {
|
|||||||
match &mut self.state {
|
match &mut self.state {
|
||||||
DummyState::Empty => {
|
DummyState::Empty => {
|
||||||
tracing::trace!("Dummy initiator is empty, initializing…");
|
tracing::trace!("Dummy initiator is empty, initializing…");
|
||||||
mem::replace(
|
self.state = DummyState::Sleeping(Self::timer(), Some(Status::Free));
|
||||||
&mut self.state,
|
|
||||||
DummyState::Sleeping(Self::timer(), Some(Status::Free)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
DummyState::Sleeping(timer, next) => {
|
DummyState::Sleeping(timer, next) => {
|
||||||
tracing::trace!("Sleep timer exists, polling it.");
|
tracing::trace!("Sleep timer exists, polling it.");
|
||||||
@ -78,7 +72,7 @@ impl Future for Dummy {
|
|||||||
|
|
||||||
let status = next.take().unwrap();
|
let status = next.take().unwrap();
|
||||||
let f = self.flip(status);
|
let f = self.flip(status);
|
||||||
mem::replace(&mut self.state, DummyState::Updating(f));
|
self.state = DummyState::Updating(f);
|
||||||
}
|
}
|
||||||
DummyState::Updating(f) => {
|
DummyState::Updating(f) => {
|
||||||
tracing::trace!("Update future exists, polling it .");
|
tracing::trace!("Update future exists, polling it .");
|
||||||
@ -87,10 +81,7 @@ impl Future for Dummy {
|
|||||||
|
|
||||||
tracing::trace!("Update future completed, sleeping!");
|
tracing::trace!("Update future completed, sleeping!");
|
||||||
|
|
||||||
mem::replace(
|
self.state = DummyState::Sleeping(Self::timer(), Some(next));
|
||||||
&mut self.state,
|
|
||||||
DummyState::Sleeping(Self::timer(), Some(next)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,22 +3,15 @@ use crate::initiators::process::Process;
|
|||||||
use crate::resources::modules::fabaccess::Status;
|
use crate::resources::modules::fabaccess::Status;
|
||||||
use crate::session::SessionHandle;
|
use crate::session::SessionHandle;
|
||||||
use crate::{
|
use crate::{
|
||||||
AuthenticationHandle, Config, MachineState, Resource, ResourcesHandle, SessionManager,
|
AuthenticationHandle, Config, Resource, ResourcesHandle, SessionManager,
|
||||||
};
|
};
|
||||||
use async_compat::CompatExt;
|
|
||||||
use executor::prelude::Executor;
|
use executor::prelude::Executor;
|
||||||
use futures_util::ready;
|
use futures_util::ready;
|
||||||
use miette::IntoDiagnostic;
|
|
||||||
use rumqttc::ConnectReturnCode::Success;
|
|
||||||
use rumqttc::{AsyncClient, ConnectionError, Event, Incoming, MqttOptions};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Display;
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use std::time::Duration;
|
|
||||||
use tracing::Span;
|
use tracing::Span;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
mod dummy;
|
mod dummy;
|
||||||
mod process;
|
mod process;
|
||||||
@ -107,7 +100,7 @@ pub fn load(
|
|||||||
config: &Config,
|
config: &Config,
|
||||||
resources: ResourcesHandle,
|
resources: ResourcesHandle,
|
||||||
sessions: SessionManager,
|
sessions: SessionManager,
|
||||||
authentication: AuthenticationHandle,
|
_authentication: AuthenticationHandle,
|
||||||
) -> miette::Result<()> {
|
) -> miette::Result<()> {
|
||||||
let span = tracing::info_span!("loading initiators");
|
let span = tracing::info_span!("loading initiators");
|
||||||
let _guard = span.enter();
|
let _guard = span.enter();
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
use super::Initiator;
|
use super::Initiator;
|
||||||
use super::InitiatorCallbacks;
|
use super::InitiatorCallbacks;
|
||||||
use crate::resources::modules::fabaccess::Status;
|
use crate::resources::modules::fabaccess::Status;
|
||||||
use crate::resources::state::State;
|
|
||||||
use crate::utils::linebuffer::LineBuffer;
|
use crate::utils::linebuffer::LineBuffer;
|
||||||
use async_process::{Child, ChildStderr, ChildStdout, Command, Stdio};
|
use async_process::{Child, ChildStderr, ChildStdout, Command, Stdio};
|
||||||
use futures_lite::{ready, AsyncRead};
|
use futures_lite::AsyncRead;
|
||||||
use miette::{miette, IntoDiagnostic};
|
use miette::{miette, IntoDiagnostic};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -117,7 +116,7 @@ impl ProcessState {
|
|||||||
impl Future for Process {
|
impl Future for Process {
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
if let Process {
|
if let Process {
|
||||||
state: Some(state),
|
state: Some(state),
|
||||||
buffer,
|
buffer,
|
||||||
|
17
bffhd/lib.rs
17
bffhd/lib.rs
@ -3,13 +3,12 @@
|
|||||||
//#![warn(missing_docs)]
|
//#![warn(missing_docs)]
|
||||||
//#![warn(missing_crate_level_docs)]
|
//#![warn(missing_crate_level_docs)]
|
||||||
|
|
||||||
//! Diflouroborane
|
//! Difluoroborane
|
||||||
//!
|
//!
|
||||||
//! This is the capnp component of the FabAccess project.
|
//! This is the capnp component of the FabAccess project.
|
||||||
//! The entry point of bffhd can be found in [bin/bffhd/main.rs](../bin/bffhd/main.rs)
|
//! The entry point of bffhd can be found in [bin/bffhd/main.rs](../bin/bffhd/main.rs)
|
||||||
|
|
||||||
use miette::Diagnostic;
|
use miette::Diagnostic;
|
||||||
use std::io;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
@ -48,7 +47,6 @@ mod tls;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use futures_util::{FutureExt, StreamExt};
|
use futures_util::{FutureExt, StreamExt};
|
||||||
use miette::{Context, IntoDiagnostic, Report};
|
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
use crate::audit::AuditLog;
|
use crate::audit::AuditLog;
|
||||||
@ -69,7 +67,7 @@ use lightproc::recoverable_handle::RecoverableHandle;
|
|||||||
use signal_hook::consts::signal::*;
|
use signal_hook::consts::signal::*;
|
||||||
use tracing::Span;
|
use tracing::Span;
|
||||||
|
|
||||||
pub struct Diflouroborane {
|
pub struct Difluoroborane {
|
||||||
config: Config,
|
config: Config,
|
||||||
executor: Executor<'static>,
|
executor: Executor<'static>,
|
||||||
pub statedb: StateDB,
|
pub statedb: StateDB,
|
||||||
@ -89,6 +87,7 @@ impl error::Description for SignalHandlerErr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error, Diagnostic)]
|
#[derive(Debug, Error, Diagnostic)]
|
||||||
|
// TODO 0.5: #[non_exhaustive]
|
||||||
pub enum BFFHError {
|
pub enum BFFHError {
|
||||||
#[error("DB operation failed")]
|
#[error("DB operation failed")]
|
||||||
DBError(
|
DBError(
|
||||||
@ -136,7 +135,7 @@ pub enum BFFHError {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Diflouroborane {
|
impl Difluoroborane {
|
||||||
pub fn setup() {}
|
pub fn setup() {}
|
||||||
|
|
||||||
pub fn new(config: Config) -> Result<Self, BFFHError> {
|
pub fn new(config: Config) -> Result<Self, BFFHError> {
|
||||||
@ -212,7 +211,9 @@ impl Diflouroborane {
|
|||||||
self.resources.clone(),
|
self.resources.clone(),
|
||||||
sessionmanager.clone(),
|
sessionmanager.clone(),
|
||||||
authentication.clone(),
|
authentication.clone(),
|
||||||
);
|
).expect("initializing initiators failed");
|
||||||
|
// TODO 0.5: error handling. Add variant to BFFHError
|
||||||
|
|
||||||
actors::load(self.executor.clone(), &self.config, self.resources.clone())?;
|
actors::load(self.executor.clone(), &self.config, self.resources.clone())?;
|
||||||
|
|
||||||
let tlsconfig = TlsConfig::new(self.config.tlskeylog.as_ref(), !self.config.is_quiet())?;
|
let tlsconfig = TlsConfig::new(self.config.tlskeylog.as_ref(), !self.config.is_quiet())?;
|
||||||
@ -231,13 +232,13 @@ impl Diflouroborane {
|
|||||||
self.executor.spawn(apiserver.handle_until(rx));
|
self.executor.spawn(apiserver.handle_until(rx));
|
||||||
|
|
||||||
let f = async {
|
let f = async {
|
||||||
let mut sig = None;
|
let mut sig;
|
||||||
while {
|
while {
|
||||||
sig = signals.next().await;
|
sig = signals.next().await;
|
||||||
sig.is_none()
|
sig.is_none()
|
||||||
} {}
|
} {}
|
||||||
tracing::info!(signal = %sig.unwrap(), "Received signal");
|
tracing::info!(signal = %sig.unwrap(), "Received signal");
|
||||||
tx.send(());
|
_ = tx.send(()); // ignore result, as an Err means that the executor we want to stop has already stopped
|
||||||
};
|
};
|
||||||
|
|
||||||
self.executor.run(f);
|
self.executor.run(f);
|
||||||
|
@ -2,8 +2,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use tracing_subscriber::fmt::format::Format;
|
use tracing_subscriber::fmt::format::Format;
|
||||||
use tracing_subscriber::prelude::*;
|
use tracing_subscriber::prelude::*;
|
||||||
use tracing_subscriber::reload::Handle;
|
use tracing_subscriber::EnvFilter;
|
||||||
use tracing_subscriber::{reload, EnvFilter};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct LogConfig {
|
pub struct LogConfig {
|
||||||
|
@ -85,10 +85,13 @@ impl Inner {
|
|||||||
self.db.put(&self.id.as_bytes(), &state).unwrap();
|
self.db.put(&self.id.as_bytes(), &state).unwrap();
|
||||||
tracing::trace!("Updated DB, sending update signal");
|
tracing::trace!("Updated DB, sending update signal");
|
||||||
|
|
||||||
AUDIT
|
let res = AUDIT
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.log(self.id.as_str(), &format!("{}", state));
|
.log(self.id.as_str(), &format!("{}", state));
|
||||||
|
if let Err(e) = res {
|
||||||
|
tracing::error!("Writing to the audit log failed for {} {}: {e}", self.id.as_str(), state);
|
||||||
|
}
|
||||||
|
|
||||||
self.signal.set(state);
|
self.signal.set(state);
|
||||||
tracing::trace!("Sent update signal");
|
tracing::trace!("Sent update signal");
|
||||||
@ -161,7 +164,7 @@ impl Resource {
|
|||||||
|
|
||||||
fn set_state(&self, state: MachineState) {
|
fn set_state(&self, state: MachineState) {
|
||||||
let mut serializer = AllocSerializer::<1024>::default();
|
let mut serializer = AllocSerializer::<1024>::default();
|
||||||
serializer.serialize_value(&state);
|
serializer.serialize_value(&state).expect("serializing a MachineState shoud be infallible");
|
||||||
let archived = ArchivedValue::new(serializer.into_serializer().into_inner());
|
let archived = ArchivedValue::new(serializer.into_serializer().into_inner());
|
||||||
self.inner.set_state(archived)
|
self.inner.set_state(archived)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ use crate::utils::oid::ObjectIdentifier;
|
|||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use rkyv::{Archive, Archived, Deserialize, Infallible};
|
use rkyv::{Archive, Archived, Deserialize, Infallible};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Write;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
//use crate::oidvalue;
|
//use crate::oidvalue;
|
||||||
|
@ -3,10 +3,8 @@ use thiserror::Error;
|
|||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::db::{AlignedAdapter, ArchivedValue, RawDB, DB};
|
use crate::db::{AlignedAdapter, ArchivedValue, RawDB, DB};
|
||||||
use lmdb::{DatabaseFlags, Environment, EnvironmentFlags, Transaction, WriteFlags};
|
use lmdb::{DatabaseFlags, Environment, EnvironmentFlags, Transaction, WriteFlags};
|
||||||
use miette::{Diagnostic, LabeledSpan, Severity, SourceCode};
|
use miette::Diagnostic;
|
||||||
use std::any::TypeId;
|
use std::fmt::Debug;
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
|
||||||
use std::{path::Path, sync::Arc};
|
use std::{path::Path, sync::Arc};
|
||||||
|
|
||||||
use crate::resources::state::State;
|
use crate::resources::state::State;
|
||||||
@ -54,8 +52,8 @@ impl StateDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_with_env(env: Arc<Environment>) -> Result<Self, StateDBError> {
|
pub fn open_with_env(env: Arc<Environment>) -> Result<Self, StateDBError> {
|
||||||
let db = unsafe { RawDB::open(&env, Some("state")) };
|
let db = RawDB::open(&env, Some("state"))
|
||||||
let db = db.map_err(|e| StateDBError::Open(e.into()))?;
|
.map_err(|e| StateDBError::Open(e.into()))?;
|
||||||
Ok(Self::new(env, db))
|
Ok(Self::new(env, db))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,8 +64,8 @@ impl StateDB {
|
|||||||
|
|
||||||
pub fn create_with_env(env: Arc<Environment>) -> Result<Self, StateDBError> {
|
pub fn create_with_env(env: Arc<Environment>) -> Result<Self, StateDBError> {
|
||||||
let flags = DatabaseFlags::empty();
|
let flags = DatabaseFlags::empty();
|
||||||
let db = unsafe { RawDB::create(&env, Some("state"), flags) };
|
let db = RawDB::create(&env, Some("state"), flags)
|
||||||
let db = db.map_err(|e| StateDBError::Create(e.into()))?;
|
.map_err(|e| StateDBError::Create(e.into()))?;
|
||||||
|
|
||||||
Ok(Self::new(env, db))
|
Ok(Self::new(env, db))
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::{fmt, hash::Hasher};
|
use std::fmt;
|
||||||
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
@ -14,8 +14,6 @@ use inventory;
|
|||||||
|
|
||||||
use rkyv::ser::{ScratchSpace, Serializer};
|
use rkyv::ser::{ScratchSpace, Serializer};
|
||||||
|
|
||||||
use serde::ser::SerializeMap;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -2,7 +2,6 @@ use lmdb::{DatabaseFlags, Environment, RwTransaction, Transaction, WriteFlags};
|
|||||||
use rkyv::Infallible;
|
use rkyv::Infallible;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use miette::{Context, IntoDiagnostic};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::db;
|
use crate::db;
|
||||||
@ -183,8 +182,8 @@ impl UserDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_txn(&self, txn: &mut RwTransaction) -> Result<(), db::Error> {
|
pub fn clear_txn(&self, txn: &mut RwTransaction) -> Result<(), db::Error> {
|
||||||
self.db.clear(txn);
|
// TODO: why was the result ignored here?
|
||||||
Ok(())
|
self.db.clear(txn)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all(&self) -> Result<HashMap<String, UserData>, db::Error> {
|
pub fn get_all(&self) -> Result<HashMap<String, UserData>, db::Error> {
|
||||||
|
@ -7,8 +7,7 @@ use std::collections::HashMap;
|
|||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use clap::ArgMatches;
|
use miette::{Diagnostic, IntoDiagnostic, SourceSpan};
|
||||||
use miette::{Context, Diagnostic, IntoDiagnostic, SourceOffset, SourceSpan};
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use clap::{Arg, Command, ValueHint};
|
use clap::{Arg, Command, ValueHint};
|
||||||
use diflouroborane::{config, Diflouroborane};
|
use difluoroborane::{config, Difluoroborane};
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{env, io, io::Write, path::PathBuf};
|
use std::{env, io, io::Write, path::PathBuf};
|
||||||
@ -15,12 +15,12 @@ fn main() -> miette::Result<()> {
|
|||||||
FabAccess {apiver}\n\
|
FabAccess {apiver}\n\
|
||||||
\t[{build_kind} build built on {build_time}]\n\
|
\t[{build_kind} build built on {build_time}]\n\
|
||||||
\t {rustc_version}\n\t {cargo_version}",
|
\t {rustc_version}\n\t {cargo_version}",
|
||||||
version=diflouroborane::env::PKG_VERSION,
|
version=difluoroborane::env::PKG_VERSION,
|
||||||
apiver="0.3",
|
apiver="0.3",
|
||||||
rustc_version=diflouroborane::env::RUST_VERSION,
|
rustc_version=difluoroborane::env::RUST_VERSION,
|
||||||
cargo_version=diflouroborane::env::CARGO_VERSION,
|
cargo_version=difluoroborane::env::CARGO_VERSION,
|
||||||
build_time=diflouroborane::env::BUILD_TIME_3339,
|
build_time=difluoroborane::env::BUILD_TIME_3339,
|
||||||
build_kind=diflouroborane::env::BUILD_RUST_CHANNEL))
|
build_kind=difluoroborane::env::BUILD_RUST_CHANNEL))
|
||||||
.about(clap::crate_description!())
|
.about(clap::crate_description!())
|
||||||
.arg(Arg::new("config")
|
.arg(Arg::new("config")
|
||||||
.help("Path to the config file to use")
|
.help("Path to the config file to use")
|
||||||
@ -98,7 +98,7 @@ fn main() -> miette::Result<()> {
|
|||||||
|
|
||||||
let configpath = matches
|
let configpath = matches
|
||||||
.value_of("config")
|
.value_of("config")
|
||||||
.unwrap_or("/etc/diflouroborane.dhall");
|
.unwrap_or("/etc/difluoroborane.dhall");
|
||||||
|
|
||||||
// Check for the --print-default option first because we don't need to do anything else in that
|
// Check for the --print-default option first because we don't need to do anything else in that
|
||||||
// case.
|
// case.
|
||||||
@ -140,7 +140,7 @@ fn main() -> miette::Result<()> {
|
|||||||
if matches.is_present("dump") {
|
if matches.is_present("dump") {
|
||||||
return Err(miette::miette!("DB Dumping is currently not implemented, except for the users db, using `--dump-users`"));
|
return Err(miette::miette!("DB Dumping is currently not implemented, except for the users db, using `--dump-users`"));
|
||||||
} else if matches.is_present("dump-users") {
|
} else if matches.is_present("dump-users") {
|
||||||
let bffh = Diflouroborane::new(config)?;
|
let bffh = Difluoroborane::new(config)?;
|
||||||
|
|
||||||
let number = bffh.users.dump_file(
|
let number = bffh.users.dump_file(
|
||||||
matches.value_of("dump-users").unwrap(),
|
matches.value_of("dump-users").unwrap(),
|
||||||
@ -151,7 +151,7 @@ fn main() -> miette::Result<()> {
|
|||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else if matches.is_present("load") {
|
} else if matches.is_present("load") {
|
||||||
let bffh = Diflouroborane::new(config)?;
|
let bffh = Difluoroborane::new(config)?;
|
||||||
|
|
||||||
bffh.users.load_file(matches.value_of("load").unwrap())?;
|
bffh.users.load_file(matches.value_of("load").unwrap())?;
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ fn main() -> miette::Result<()> {
|
|||||||
}
|
}
|
||||||
config.logging.format = matches.value_of("log format").unwrap_or("full").to_string();
|
config.logging.format = matches.value_of("log format").unwrap_or("full").to_string();
|
||||||
|
|
||||||
let mut bffh = Diflouroborane::new(config)?;
|
let mut bffh = Difluoroborane::new(config)?;
|
||||||
bffh.run()?;
|
bffh.run()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
build.rs
2
build.rs
@ -1,4 +1,4 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
// Extract build-time information using the `shadow-rs` crate
|
// Extract build-time information using the `shadow-rs` crate
|
||||||
shadow_rs::new();
|
shadow_rs::new().unwrap();
|
||||||
}
|
}
|
||||||
|
111
docs/decisions/0000-Programming-language-and-framework-choice.md
Normal file
111
docs/decisions/0000-Programming-language-and-framework-choice.md
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
# Choosing a programming Language and Framework for BFFH
|
||||||
|
|
||||||
|
* Status: accepted <!-- optional -->
|
||||||
|
* Date: 18.02.2020 (?) <!-- optional -->
|
||||||
|
|
||||||
|
Technical Story: Decision regarding programming language and framework to use in BFFH / the backend code of FabAccess <!-- optional -->
|
||||||
|
|
||||||
|
## Context and Problem Statement
|
||||||
|
|
||||||
|
Programming language Dicussions are the perfect [bikeshedding](http://catb.org/jargon/html/B/bikeshedding.html) topic.
|
||||||
|
Regardless at some point a decision has to be made so that people can start writing code and being generally useful.
|
||||||
|
Since FabAccess started as a project with several potential developers and even more stakeholders interested/relying on
|
||||||
|
the success of the software this discussion was *extra* spicy.
|
||||||
|
The relevant discussions were had in the timeframe from about November of 2019 to March of 2020.
|
||||||
|
|
||||||
|
## Decision Drivers <!-- optional -->
|
||||||
|
|
||||||
|
* Available developers, both short-term and medium-term should the project become a successful staple of
|
||||||
|
german/european/terran makerspaces
|
||||||
|
* Barrier of entry for new / additional developers.
|
||||||
|
* Tooling support, esp. regarding non-developers (documentation, ease of self-compilation, …)
|
||||||
|
* Library support / ecosystem in the problem domain
|
||||||
|
* Existing projects that could be extended as to not invent the wheel again
|
||||||
|
* Language Features
|
||||||
|
|
||||||
|
## Considered Options
|
||||||
|
|
||||||
|
* Python
|
||||||
|
* TypeScript
|
||||||
|
* Rust
|
||||||
|
* Erlang
|
||||||
|
* Ruby
|
||||||
|
* Haskell
|
||||||
|
|
||||||
|
## Decision Outcome
|
||||||
|
|
||||||
|
Chosen option: "Rust", because the project ended up mostly being developed by FabInfra whos cost/benefit analisys skews
|
||||||
|
in favour of type safety over speed of development.
|
||||||
|
|
||||||
|
### Positive Consequences <!-- optional -->
|
||||||
|
|
||||||
|
* Type safety of Rust strong compared to all other options barring Haskell
|
||||||
|
* Single statically linked binary with very few dependencies makes deployment trivial
|
||||||
|
* Compiled software close to the metal allows running the server on low-powered hardware such as Raspberry Pis even for
|
||||||
|
medium-sized deployments.
|
||||||
|
|
||||||
|
### Negative Consequences <!-- optional -->
|
||||||
|
|
||||||
|
* Speed of development suffers short-term. Rust is a lower-level language than all other evaluated options and requires
|
||||||
|
more thought and developer time to implement high level features.
|
||||||
|
* Available developer pool is small and expensive. Rust is not a good first programming language and comparatively hard
|
||||||
|
to get into for developers if they don't have experience with C++. Developers are available to hire but salary
|
||||||
|
expectation of Rust developers is high compared to Python / Java.
|
||||||
|
* Small ecosystem / few libraries. Rust is a very young programming language and despite being rated as the most popular
|
||||||
|
language on SO for several years and being commercially used has fewer libraries than most other options. Support for
|
||||||
|
e.g. LDAP is not mature. But problem domain of FabAccess is in the area that Rust has the most support for.
|
||||||
|
|
||||||
|
## Pros and Cons of the Options <!-- optional -->
|
||||||
|
|
||||||
|
### Python
|
||||||
|
|
||||||
|
* Good, because large available developer pool
|
||||||
|
* Good, because of the developers interested in developing it large parts have experience with Python
|
||||||
|
* Good, because Python is easy to learn and a good first language
|
||||||
|
* Good, because Python has extensive library support and an ecosystem geared towards automation
|
||||||
|
* Bad, because comparatively slow. Slowest or second slowest option considered.
|
||||||
|
* Bad, because highly dynamic typing and mediocre static analysis has potential for edgecase bugs being hidden for long
|
||||||
|
times
|
||||||
|
* Bad, because Python gives little control over the crash process. Catching exceptions is very coarse.
|
||||||
|
|
||||||
|
### TypeScript
|
||||||
|
|
||||||
|
* Good, because stronger typing than Python and Ruby
|
||||||
|
* Bad, because interpreted language and while faster than Ruby/Python still much slower than the compiled options.
|
||||||
|
* Bad, because the Node.js ecosystem is large but not all that much geared towards low level automation
|
||||||
|
|
||||||
|
### Rust
|
||||||
|
|
||||||
|
* Good, because Rust has a good type system allowing to prevent many bugs and crashes using static analysis built
|
||||||
|
into the compiler.
|
||||||
|
* Good, because Rust code is very efficient.
|
||||||
|
* Good, because many people are very interested in learning Rust and are only lacking a reason to.
|
||||||
|
* Bad, because speed of development is slower than for most other options.
|
||||||
|
* Bad, because only one developer in the group has any experience with Rust.
|
||||||
|
* Bad, because available developer pool is small.
|
||||||
|
|
||||||
|
### Erlang
|
||||||
|
|
||||||
|
* Good, because specifically built for highly-available, crash-resistant software
|
||||||
|
* Good, because it offers an extremely fine-grained crashing system with encapsulated processes that can not take each
|
||||||
|
other down
|
||||||
|
* Good, because the ecosystem and libraries of Erlang are generally geared towards the problem domain of FabAccess
|
||||||
|
* Good, because Erlang allows for significant static analysis compared to TypeScript, Pyton and Ruby
|
||||||
|
* Bad, because Erlang has a worse type system than Rust and Haskell.
|
||||||
|
* Bad, because only one developer in the group has any experience with Erlang.
|
||||||
|
* Bad, because the developer pool is the second smallest.
|
||||||
|
|
||||||
|
### Haskell
|
||||||
|
|
||||||
|
* Good, because strongest type system allowing to prevent many bugs and crashes using static analysis built into the
|
||||||
|
compiler
|
||||||
|
* Bad, because only one developer has any experience with Haskell.
|
||||||
|
* Bad, because the developer pool is the smallest.
|
||||||
|
|
||||||
|
## Links <!-- optional -->
|
||||||
|
|
||||||
|
* Discussions documented in:
|
||||||
|
- [Protokoll Jit.si-Konferenz 09.12.19](https://pad.gwdg.de/obbgQwKiQNmRRwDt1yERew?view#Kuratiertes-Framework-vs-Erweiterbarer-Monolith)
|
||||||
|
- [Pad "Roseguarden"](https://pad.gwdg.de/v-xVnpWQREmBGuLG_hYTfQ#Projektinterne-Werkzeuge)
|
||||||
|
- [Pad BF²H "Grundlegendes"](https://pad.gwdg.de/XrReiGdCS-GfcWhEKgjTBA#)
|
||||||
|
- [JitSi Call 18.02](https://pad.gwdg.de/71fd449SRgGm_HhL4rgVqw#)
|
14
docs/decisions/index.md
Normal file
14
docs/decisions/index.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Architectural Decision Log
|
||||||
|
|
||||||
|
This log lists the architectural decisions for Diflouroborane (BFFH).
|
||||||
|
See also the ADR log of [FabAccess-API](https://gitlab.com/fabinfra/fabaccess/fabaccess-api/-/tree/main/docs/decisions).
|
||||||
|
|
||||||
|
<!-- adrlog -- Regenerate the content by using "adr-log -i". You can install it via "npm install -g adr-log" -->
|
||||||
|
|
||||||
|
* [ADR-0000](0000-Programming-language-and-framework-choice.md) - Choosing a programming Language and Framework for BFFH
|
||||||
|
|
||||||
|
<!-- adrlogstop -->
|
||||||
|
|
||||||
|
For new ADRs, please use [template.md](template.md) as basis.
|
||||||
|
More information on MADR is available at <https://adr.github.io/madr/>.
|
||||||
|
General information about architectural decision records is available at <https://adr.github.io/>.
|
72
docs/decisions/template.md
Normal file
72
docs/decisions/template.md
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# [short title of solved problem and solution]
|
||||||
|
|
||||||
|
* Status: [proposed | rejected | accepted | deprecated | … | superseded by [ADR-0005](0005-example.md)] <!-- optional -->
|
||||||
|
* Deciders: [list everyone involved in the decision] <!-- optional -->
|
||||||
|
* Date: [YYYY-MM-DD when the decision was last updated] <!-- optional -->
|
||||||
|
|
||||||
|
Technical Story: [description | ticket/issue URL] <!-- optional -->
|
||||||
|
|
||||||
|
## Context and Problem Statement
|
||||||
|
|
||||||
|
[Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question.]
|
||||||
|
|
||||||
|
## Decision Drivers <!-- optional -->
|
||||||
|
|
||||||
|
* [driver 1, e.g., a force, facing concern, …]
|
||||||
|
* [driver 2, e.g., a force, facing concern, …]
|
||||||
|
* … <!-- numbers of drivers can vary -->
|
||||||
|
|
||||||
|
## Considered Options
|
||||||
|
|
||||||
|
* [option 1]
|
||||||
|
* [option 2]
|
||||||
|
* [option 3]
|
||||||
|
* … <!-- numbers of options can vary -->
|
||||||
|
|
||||||
|
## Decision Outcome
|
||||||
|
|
||||||
|
Chosen option: "[option 1]", because [justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force force | … | comes out best (see below)].
|
||||||
|
|
||||||
|
### Positive Consequences <!-- optional -->
|
||||||
|
|
||||||
|
* [e.g., improvement of quality attribute satisfaction, follow-up decisions required, …]
|
||||||
|
* …
|
||||||
|
|
||||||
|
### Negative Consequences <!-- optional -->
|
||||||
|
|
||||||
|
* [e.g., compromising quality attribute, follow-up decisions required, …]
|
||||||
|
* …
|
||||||
|
|
||||||
|
## Pros and Cons of the Options <!-- optional -->
|
||||||
|
|
||||||
|
### [option 1]
|
||||||
|
|
||||||
|
[example | description | pointer to more information | …] <!-- optional -->
|
||||||
|
|
||||||
|
* Good, because [argument a]
|
||||||
|
* Good, because [argument b]
|
||||||
|
* Bad, because [argument c]
|
||||||
|
* … <!-- numbers of pros and cons can vary -->
|
||||||
|
|
||||||
|
### [option 2]
|
||||||
|
|
||||||
|
[example | description | pointer to more information | …] <!-- optional -->
|
||||||
|
|
||||||
|
* Good, because [argument a]
|
||||||
|
* Good, because [argument b]
|
||||||
|
* Bad, because [argument c]
|
||||||
|
* … <!-- numbers of pros and cons can vary -->
|
||||||
|
|
||||||
|
### [option 3]
|
||||||
|
|
||||||
|
[example | description | pointer to more information | …] <!-- optional -->
|
||||||
|
|
||||||
|
* Good, because [argument a]
|
||||||
|
* Good, because [argument b]
|
||||||
|
* Bad, because [argument c]
|
||||||
|
* … <!-- numbers of pros and cons can vary -->
|
||||||
|
|
||||||
|
## Links <!-- optional -->
|
||||||
|
|
||||||
|
* [Link type] [Link to ADR] <!-- example: Refined by [ADR-0005](0005-example.md) -->
|
||||||
|
* … <!-- numbers of links can vary -->
|
Loading…
Reference in New Issue
Block a user