mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-12-30 15:33:48 +01:00
Stuff
This commit is contained in:
parent
bedde0e19f
commit
8db5580c90
@ -38,7 +38,7 @@ pub mod user {
|
||||
}
|
||||
|
||||
pub mod users {
|
||||
pub use crate::schema::users_capnp::*;
|
||||
pub use crate::schema::users_capnp::users::*;
|
||||
}
|
||||
|
||||
pub mod utils {
|
||||
|
12
bffhd/db.rs
12
bffhd/db.rs
@ -1,6 +1,4 @@
|
||||
use std::{
|
||||
marker::PhantomData,
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub use lmdb::{
|
||||
Environment,
|
||||
@ -39,14 +37,10 @@ mod fix;
|
||||
pub use fix::LMDBorrow;
|
||||
|
||||
mod resources;
|
||||
pub use resources::{
|
||||
ResourceDB,
|
||||
};
|
||||
pub use resources::ResourceDB;
|
||||
|
||||
mod pass;
|
||||
pub use pass::{
|
||||
PassDB,
|
||||
};
|
||||
pub use pass::PassDB;
|
||||
|
||||
use lmdb::Error;
|
||||
use rkyv::Deserialize;
|
||||
|
@ -1,8 +1,6 @@
|
||||
use rkyv::{Archive, Serialize, Deserialize};
|
||||
|
||||
use super::{
|
||||
DB,
|
||||
};
|
||||
use super::DB;
|
||||
use crate::db::{AlignedAdapter, AllocAdapter};
|
||||
use crate::db::raw::RawDB;
|
||||
use std::sync::Arc;
|
||||
|
@ -1,5 +1,7 @@
|
||||
#![forbid(unused_imports)]
|
||||
#![forbid(unused_imports, unused_import_braces)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
#![warn(missing_docs)]
|
||||
#![warn(missing_crate_level_docs)]
|
||||
|
||||
//! Diflouroborane
|
||||
//!
|
||||
|
@ -3,7 +3,7 @@ use std::{
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use rkyv::{Archived};
|
||||
use rkyv::Archived;
|
||||
|
||||
use crate::db::{
|
||||
DB,
|
||||
|
@ -1,8 +1,6 @@
|
||||
use core::{
|
||||
ptr,
|
||||
};
|
||||
use std::{
|
||||
fmt,
|
||||
ptr,
|
||||
any::Any,
|
||||
hash::Hash,
|
||||
str::FromStr,
|
||||
@ -14,7 +12,7 @@ use rkyv_typename::TypeName;
|
||||
use ptr_meta::{DynMetadata, Pointee};
|
||||
|
||||
use inventory;
|
||||
use crate::utils::oid::{ObjectIdentifier};
|
||||
use crate::utils::oid::ObjectIdentifier;
|
||||
use rkyv::ser::{Serializer, ScratchSpace};
|
||||
use std::collections::HashMap;
|
||||
use std::alloc::Layout;
|
||||
|
@ -5,7 +5,7 @@ use capnp::capability::Promise;
|
||||
use capnp::Error;
|
||||
use capnp_rpc::pry;
|
||||
|
||||
use rsasl::{gsasl_err_to_str, SaslError, Session};
|
||||
use rsasl::{rsasl_err_to_str, SASL, Session as SaslSession, Property, ReturnCode};
|
||||
use rsasl::session::Step::{Done, NeedsMore};
|
||||
|
||||
use api::auth::authentication::{
|
||||
@ -19,17 +19,63 @@ use api::auth::response::{
|
||||
Reason,
|
||||
Action,
|
||||
};
|
||||
use crate::users::{UserDB, PassDB};
|
||||
|
||||
struct Callback;
|
||||
|
||||
pub struct Authentication {
|
||||
state: State<()>,
|
||||
struct AppData {
|
||||
userdb: UserDB,
|
||||
passdb: PassDB,
|
||||
}
|
||||
|
||||
enum State<D> {
|
||||
struct SessionData;
|
||||
|
||||
impl rsasl::Callback<AppData, SessionData> for Callback {
|
||||
fn callback(sasl: &mut SASL<AppData, SessionData>,
|
||||
session: &mut SaslSession<SessionData>,
|
||||
prop: Property
|
||||
) -> Result<(), ReturnCode>
|
||||
{
|
||||
match prop {
|
||||
Property::GSASL_VALIDATE_SIMPLE => {
|
||||
// Access the authentication id, i.e. the username to check the password for
|
||||
let authcid = session
|
||||
.get_property(Property::GSASL_AUTHID)
|
||||
.ok_or(rsasl::GSASL_NO_AUTHID)
|
||||
.map_err(|_| rsasl::GSASL_NO_AUTHID)
|
||||
.and_then(|cstr| cstr.to_str()
|
||||
.map_err(|_| rsasl::GSASL_NO_AUTHID))?;
|
||||
|
||||
// Access the password itself
|
||||
let password = session
|
||||
.get_property(Property::GSASL_PASSWORD)
|
||||
.ok_or(rsasl::GSASL_NO_PASSWORD)
|
||||
.and_then(|cstr| cstr.to_str()
|
||||
.map_err(|_| rsasl::GSASL_NO_AUTHID))?;
|
||||
|
||||
let AppData { userdb: _, passdb } = sasl.retrieve_mut()
|
||||
.ok_or(rsasl::GSASL_NO_CALLBACK)?;
|
||||
|
||||
if let Ok(Some(Ok(true))) = passdb.verify_password(authcid, &password.as_bytes()) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(rsasl::GSASL_AUTHENTICATION_ERROR)
|
||||
}
|
||||
},
|
||||
_ => Err(rsasl::GSASL_NO_CALLBACK),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Authentication {
|
||||
state: State<SessionData>,
|
||||
}
|
||||
|
||||
enum State<E> {
|
||||
InvalidMechanism,
|
||||
Finished,
|
||||
Aborted,
|
||||
Running(Session<D>)
|
||||
Running(SaslSession<E>)
|
||||
}
|
||||
|
||||
impl Server for Authentication {
|
||||
@ -37,19 +83,19 @@ impl Server for Authentication {
|
||||
use State::*;
|
||||
match self.state {
|
||||
InvalidMechanism => {
|
||||
let mut builder = results.get();
|
||||
let builder = results.get();
|
||||
let mut b = builder.init_error();
|
||||
b.set_reason(Reason::BadMechanism);
|
||||
b.set_action(Action::Permanent);
|
||||
},
|
||||
Finished => {
|
||||
let mut builder = results.get();
|
||||
let builder = results.get();
|
||||
let mut b = builder.init_error();
|
||||
b.set_reason(Reason::Finished);
|
||||
b.set_action(Action::Permanent);
|
||||
},
|
||||
Aborted => {
|
||||
let mut builder = results.get();
|
||||
let builder = results.get();
|
||||
let mut b = builder.init_error();
|
||||
b.set_reason(Reason::Aborted);
|
||||
b.set_action(Action::Permanent);
|
||||
@ -60,13 +106,19 @@ impl Server for Authentication {
|
||||
|
||||
let mut builder = results.get();
|
||||
match session.step(data) {
|
||||
Ok(Done(Data)) => {
|
||||
Ok(Done(data)) => {
|
||||
let mut b = builder.init_successful();
|
||||
if !data.is_empty() {
|
||||
b.reborrow().set_additional_data(data.deref())
|
||||
}
|
||||
let mut session_builder = b.init_session();
|
||||
let session = super::session::Session::new();
|
||||
session.build(&mut session_builder);
|
||||
},
|
||||
Ok(NeedsMore(Data)) => {
|
||||
builder.set_challenge(Data.deref());
|
||||
Ok(NeedsMore(data)) => {
|
||||
builder.set_challenge(data.deref());
|
||||
},
|
||||
Err(e) => {
|
||||
Err(_) => {
|
||||
let mut b = builder.init_error();
|
||||
b.set_reason(Reason::Aborted);
|
||||
b.set_action(Action::Permanent);
|
||||
@ -85,7 +137,7 @@ impl Server for Authentication {
|
||||
|
||||
#[repr(transparent)]
|
||||
struct SaslE {
|
||||
e: SaslError,
|
||||
e: ReturnCode,
|
||||
}
|
||||
|
||||
impl l10n_string::Server for SaslE {
|
||||
@ -98,7 +150,8 @@ impl l10n_string::Server for SaslE {
|
||||
if lang == "en" {
|
||||
let mut builder = results.get();
|
||||
builder.set_lang("en");
|
||||
builder.set_content(gsasl_err_to_str(self.e.0));
|
||||
builder.set_content(rsasl_err_to_str(self.e)
|
||||
.unwrap_or("Unknown gsasl error"));
|
||||
}
|
||||
|
||||
Promise::ok(())
|
||||
@ -109,7 +162,7 @@ impl l10n_string::Server for SaslE {
|
||||
_: l10n_string::AvailableParams,
|
||||
mut results: l10n_string::AvailableResults
|
||||
) -> Promise<(), Error> {
|
||||
let mut builder = results.get();
|
||||
let builder = results.get();
|
||||
let mut langs = builder.init_langs(1);
|
||||
langs.set(0, "en");
|
||||
Promise::ok(())
|
||||
|
@ -11,11 +11,17 @@ use api::bootstrap::{
|
||||
|
||||
mod tls;
|
||||
mod authentication;
|
||||
mod session;
|
||||
mod users;
|
||||
mod resources;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Cap'n Proto API Handler
|
||||
struct ApiSystem {
|
||||
|
||||
}
|
||||
|
||||
|
||||
impl Server for ApiSystem {
|
||||
fn mechanisms(
|
||||
&mut self,
|
||||
|
19
bffhd/server/resources.rs
Normal file
19
bffhd/server/resources.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use api::resources::resources::Server;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Resources {
|
||||
|
||||
}
|
||||
|
||||
impl Resources {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Server for Resources {
|
||||
|
||||
}
|
23
bffhd/server/session.rs
Normal file
23
bffhd/server/session.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use api::session::Builder;
|
||||
use crate::server::resources::Resources;
|
||||
use crate::server::users::Users;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Session {
|
||||
resources: Resources,
|
||||
users: Users,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
pub fn new() -> Self {
|
||||
Session {
|
||||
resources: Resources::new(),
|
||||
users: Users::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(&self, builder: &mut Builder) {
|
||||
builder.set_resources(capnp_rpc::new_client(self.resources.clone()));
|
||||
builder.set_users(capnp_rpc::new_client(self.users.clone()));
|
||||
}
|
||||
}
|
18
bffhd/server/users.rs
Normal file
18
bffhd/server/users.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use api::users::Server;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Users {
|
||||
|
||||
}
|
||||
|
||||
impl Users {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Server for Users {
|
||||
|
||||
}
|
@ -2,17 +2,23 @@ use rkyv::{Archive, Serialize, Deserialize};
|
||||
|
||||
use capnp::capability::Promise;
|
||||
use capnp::Error;
|
||||
use capnp_rpc::pry;
|
||||
|
||||
use api::user::{
|
||||
info,
|
||||
manage,
|
||||
admin,
|
||||
passwd,
|
||||
};
|
||||
|
||||
mod db;
|
||||
mod pass;
|
||||
|
||||
pub use db::UserDB;
|
||||
pub use pass::PassDB;
|
||||
|
||||
#[derive(Debug, Clone, Archive, Serialize, Deserialize, serde::Serialize, serde::Deserialize)]
|
||||
/// User API endpoint
|
||||
pub struct User {
|
||||
id: u128,
|
||||
username: String,
|
||||
@ -26,18 +32,50 @@ impl User {
|
||||
impl info::Server for User {
|
||||
fn list_roles(
|
||||
&mut self,
|
||||
params: info::ListRolesParams,
|
||||
_params: info::ListRolesParams,
|
||||
mut results: info::ListRolesResults
|
||||
) -> Promise<(), Error>
|
||||
{
|
||||
let results = results.get();
|
||||
let mut roles = results.init_roles(self.roles.len() as u32);
|
||||
|
||||
for (i, role) in self.roles.iter().enumerate() {
|
||||
let mut role_builder = roles.reborrow().get(i as u32);
|
||||
role_builder.set_name(role);
|
||||
}
|
||||
|
||||
Promise::ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl manage::Server for User {
|
||||
fn add_role(
|
||||
&mut self,
|
||||
params: manage::AddRoleParams,
|
||||
_: manage::AddRoleResults
|
||||
) -> Promise<(), Error> {
|
||||
let params = pry!(params.get());
|
||||
let name = pry!(params.get_name()).to_string();
|
||||
self.roles.push(name);
|
||||
Promise::ok(())
|
||||
}
|
||||
|
||||
fn remove_role(
|
||||
&mut self,
|
||||
params: manage::RemoveRoleParams,
|
||||
_: manage::RemoveRoleResults
|
||||
) -> Promise<(), Error> {
|
||||
let params = pry!(params.get());
|
||||
let name = pry!(params.get_name());
|
||||
self.roles.retain(|role| role != name);
|
||||
Promise::ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl admin::Server for User {
|
||||
|
||||
}
|
||||
|
||||
impl passwd::Server for User {
|
||||
|
||||
}
|
103
bffhd/users/pass.rs
Normal file
103
bffhd/users/pass.rs
Normal file
@ -0,0 +1,103 @@
|
||||
use std::sync::Arc;
|
||||
use rand::RngCore;
|
||||
use crate::db::{RawDB, DB, AllocAdapter, Environment, Result, DBError};
|
||||
use crate::db::{DatabaseFlags, WriteFlags};
|
||||
|
||||
use rkyv::{Serialize, Deserialize, Archived, Archive};
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Archive, Serialize, Deserialize)]
|
||||
pub struct Password(String);
|
||||
|
||||
fn check_password(stored: &Archived<Password>, input: &[u8]) -> argon2::Result<bool> {
|
||||
argon2::verify_encoded(stored.0.as_str(), input)
|
||||
}
|
||||
|
||||
type Adapter = AllocAdapter<Password>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
/// Internal Password Database
|
||||
pub struct PassDB {
|
||||
env: Arc<Environment>,
|
||||
db: DB<Adapter>,
|
||||
}
|
||||
|
||||
impl PassDB {
|
||||
pub unsafe fn new(env: Arc<Environment>, db: RawDB) -> Self {
|
||||
let db = DB::new_unchecked(db);
|
||||
Self { env, db }
|
||||
}
|
||||
|
||||
pub unsafe fn open(env: Arc<Environment>) -> Result<Self> {
|
||||
let db = RawDB::open(&env, Some("user"))?;
|
||||
Ok(Self::new(env, db))
|
||||
}
|
||||
|
||||
pub unsafe fn create(env: Arc<Environment>) -> Result<Self> {
|
||||
let flags = DatabaseFlags::empty();
|
||||
let db = RawDB::create(&env, Some("user"), flags)?;
|
||||
Ok(Self::new(env, db))
|
||||
}
|
||||
|
||||
/// Verify if the given password matches for the given user.
|
||||
pub fn verify_password(&self, uid: &str, password: &[u8])
|
||||
-> Result<Option<argon2::Result<bool>>>
|
||||
{
|
||||
let txn = self.env.begin_ro_txn()?;
|
||||
if let Some(stored) = self.db.get(&txn, &uid.as_bytes())? {
|
||||
Ok(Some(check_password(stored, password)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set or update a password for a given uid.
|
||||
///
|
||||
/// The given uid must not be "" and the given password must be 1..=1024 bytes.
|
||||
pub fn set_password(&self, uid: &str, password: &[u8]) -> Result<()> {
|
||||
debug_assert!(!uid.is_empty());
|
||||
debug_assert!(0 < password.len() && password.len() <= 1024);
|
||||
|
||||
let config = argon2::Config::default();
|
||||
let mut salt: [u8; 16] = [0u8; 16];
|
||||
rand::thread_rng().fill_bytes(&mut salt);
|
||||
let encoded = argon2::hash_encoded(password, &salt, &config)
|
||||
.expect("Hashing given user password failed");
|
||||
let pwd = Password(encoded);
|
||||
|
||||
let mut txn = self.env.begin_rw_txn()?;
|
||||
self.db.put(&mut txn, &uid.as_bytes(), &pwd, WriteFlags::empty())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete an password entry from the database.
|
||||
///
|
||||
/// Returns `Ok(false)` if no entry existed for the given user.
|
||||
/// Thus, if this function returns `Ok(_)` you can be sure this db contains no password hash
|
||||
/// for the given uid.
|
||||
pub fn delete_password(&self, uid: &str) -> Result<bool> {
|
||||
let mut txn = self.env.begin_rw_txn()?;
|
||||
match self.db.del(&mut txn, &uid.as_bytes()) {
|
||||
Ok(_) => Ok(true),
|
||||
Err(DBError::LMDB(lmdb::Error::NotFound)) => Ok(false),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return all entries in this db in the form [(uid, password hash)].
|
||||
pub fn get_all(&self) -> Result<Vec<(String, Password)>> {
|
||||
let txn = self.env.begin_ro_txn()?;
|
||||
let mut cursor = self.db.open_ro_cursor(&txn)?;
|
||||
let iter = cursor.iter_start();
|
||||
let mut out = Vec::new();
|
||||
let mut deserializer = rkyv::Infallible;
|
||||
for passentry in iter {
|
||||
let (uid, password) = passentry?;
|
||||
let uid = unsafe { std::str::from_utf8_unchecked(uid).to_string() };
|
||||
let password: Password = password.deserialize(&mut deserializer).unwrap();
|
||||
out.push((uid, password));
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
}
|
@ -52,10 +52,9 @@
|
||||
//! [Object Identifiers]: https://en.wikipedia.org/wiki/Object_identifier
|
||||
//! [ITU]: https://en.wikipedia.org/wiki/International_Telecommunications_Union
|
||||
|
||||
use core::convert::{TryFrom };
|
||||
|
||||
use rkyv::{Archive, Serialize};
|
||||
use rkyv::vec::{ArchivedVec, VecResolver};
|
||||
use std::convert::TryFrom;
|
||||
use std::ops::Deref;
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::default::Default;
|
||||
use std::ops::{Deref};
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VarUInt<const N: usize> {
|
||||
|
Loading…
Reference in New Issue
Block a user