mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-26 08:34:55 +01:00
Password DB and other shenanigans
This commit is contained in:
parent
7956616891
commit
7e9002aa94
@ -39,7 +39,7 @@ impl connection_capnp::bootstrap::Server for Bootstrap {
|
|||||||
// TODO: When should we allow multiple auth and how do me make sure that does not leak
|
// TODO: When should we allow multiple auth and how do me make sure that does not leak
|
||||||
// priviledges (e.g. due to previously issues caps)?
|
// priviledges (e.g. due to previously issues caps)?
|
||||||
|
|
||||||
res.get().set_auth(capnp_rpc::new_client(auth::Auth::new(self.session.clone())));
|
res.get().set_auth(capnp_rpc::new_client(auth::Auth::new(self.db.passdb.clone(), self.session.clone())));
|
||||||
|
|
||||||
Promise::ok(())
|
Promise::ok(())
|
||||||
}
|
}
|
||||||
|
@ -27,23 +27,49 @@ use crate::config::Settings;
|
|||||||
use crate::api::Session;
|
use crate::api::Session;
|
||||||
|
|
||||||
pub use crate::schema::auth_capnp;
|
pub use crate::schema::auth_capnp;
|
||||||
|
use crate::db::pass::PassDB;
|
||||||
|
|
||||||
pub struct AppData;
|
pub struct AppData {
|
||||||
|
passdb: Arc<PassDB>,
|
||||||
|
}
|
||||||
pub struct SessionData;
|
pub struct SessionData;
|
||||||
|
|
||||||
|
|
||||||
struct CB;
|
struct CB;
|
||||||
impl Callback<AppData, SessionData> for CB {
|
impl Callback<AppData, SessionData> for CB {
|
||||||
fn callback(sasl: SaslCtx<AppData, SessionData>, session: SaslSession<SessionData>, prop: Property) -> libc::c_int {
|
fn callback(mut sasl: SaslCtx<AppData, SessionData>, session: SaslSession<SessionData>, prop: Property) -> libc::c_int {
|
||||||
let ret = match prop {
|
let ret = match prop {
|
||||||
Property::GSASL_VALIDATE_SIMPLE => {
|
Property::GSASL_VALIDATE_SIMPLE => {
|
||||||
let authid = session.get_property(Property::GSASL_AUTHID).unwrap().to_string_lossy();
|
let authid = match session.get_property(Property::GSASL_AUTHID) {
|
||||||
let pass = session.get_property(Property::GSASL_PASSWORD).unwrap().to_string_lossy();
|
None => return ReturnCode::GSASL_NO_AUTHID as libc::c_int,
|
||||||
|
Some(a) => {
|
||||||
|
match a.to_str() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => return ReturnCode::GSASL_SASLPREP_ERROR as libc::c_int,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
if authid == "test" && pass == "secret" {
|
let pass = session.get_property(Property::GSASL_PASSWORD);
|
||||||
|
if pass.is_none() {
|
||||||
|
return ReturnCode::GSASL_NO_PASSWORD as libc::c_int;
|
||||||
|
}
|
||||||
|
let pass = pass.unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
if let Some(sessiondata) = sasl.retrieve_mut() {
|
||||||
|
if let Ok(Some(b)) = sessiondata.passdb.check(authid, pass.to_bytes()) {
|
||||||
|
if b {
|
||||||
ReturnCode::GSASL_OK
|
ReturnCode::GSASL_OK
|
||||||
} else {
|
} else {
|
||||||
ReturnCode::GSASL_AUTHENTICATION_ERROR
|
ReturnCode::GSASL_AUTHENTICATION_ERROR
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ReturnCode::GSASL_AUTHENTICATION_ERROR
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReturnCode::GSASL_AUTHENTICATION_ERROR
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p => {
|
p => {
|
||||||
println!("Callback called with property {:?}", p);
|
println!("Callback called with property {:?}", p);
|
||||||
@ -60,10 +86,10 @@ pub struct Auth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Auth {
|
impl Auth {
|
||||||
pub fn new(session: Arc<Session>) -> Self {
|
pub fn new(passdb: Arc<PassDB>, session: Arc<Session>) -> Self {
|
||||||
let mut ctx = SASL::new().unwrap();
|
let mut ctx = SASL::new().unwrap();
|
||||||
|
|
||||||
let mut appdata = Box::new(AppData);
|
let mut appdata = Box::new(AppData { passdb });
|
||||||
|
|
||||||
ctx.store(appdata);
|
ctx.store(appdata);
|
||||||
|
|
||||||
|
13
src/db.rs
13
src/db.rs
@ -1,25 +1,24 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// (Hashed) password database
|
||||||
|
pub mod pass;
|
||||||
|
|
||||||
|
/// User storage
|
||||||
|
pub mod user;
|
||||||
|
|
||||||
/// Access control storage
|
/// Access control storage
|
||||||
///
|
///
|
||||||
/// Stores&Retrieves Permissions and Roles
|
/// Stores&Retrieves Permissions and Roles
|
||||||
pub mod access;
|
pub mod access;
|
||||||
/// User storage
|
|
||||||
///
|
|
||||||
/// Stores&Retrieves Users
|
|
||||||
pub mod user;
|
|
||||||
|
|
||||||
/// Machine storage
|
/// Machine storage
|
||||||
///
|
///
|
||||||
/// Stores&Retrieves Machines
|
/// Stores&Retrieves Machines
|
||||||
pub mod machine;
|
pub mod machine;
|
||||||
|
|
||||||
/// Authenticate users
|
|
||||||
pub mod pass;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Databases {
|
pub struct Databases {
|
||||||
pub access: Arc<access::AccessControl>,
|
pub access: Arc<access::AccessControl>,
|
||||||
pub machine: Arc<machine::MachineDB>,
|
pub machine: Arc<machine::MachineDB>,
|
||||||
|
pub passdb: Arc<pass::PassDB>,
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,18 @@ impl PassDB {
|
|||||||
Self { log, env, db }
|
Self { log, env, db }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check<T: Transaction>(&self, txn: &T, authcid: &str, password: &[u8]) -> Result<Option<bool>> {
|
pub fn init(log: Logger, env: Arc<Environment>) -> Result<Self> {
|
||||||
|
let mut flags = lmdb::DatabaseFlags::empty();
|
||||||
|
flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true);
|
||||||
|
let db = env.create_db(Some("pass"), flags)?;
|
||||||
|
|
||||||
|
Ok(Self::new(log, env, db))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check a password for a given authcid.
|
||||||
|
///
|
||||||
|
/// `Ok(None)` means the given authcid is not stored in the database
|
||||||
|
pub fn check_with_txn<T: Transaction>(&self, txn: &T, authcid: &str, password: &[u8]) -> Result<Option<bool>> {
|
||||||
match txn.get(self.db, &authcid.as_bytes()) {
|
match txn.get(self.db, &authcid.as_bytes()) {
|
||||||
Ok(bytes) => {
|
Ok(bytes) => {
|
||||||
let encoded = unsafe { std::str::from_utf8_unchecked(bytes) };
|
let encoded = unsafe { std::str::from_utf8_unchecked(bytes) };
|
||||||
@ -29,8 +40,13 @@ impl PassDB {
|
|||||||
Err(e) => { Err(e.into()) },
|
Err(e) => { Err(e.into()) },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn check(&self, authcid: &str, password: &[u8]) -> Result<Option<bool>> {
|
||||||
|
let txn = self.env.begin_ro_txn()?;
|
||||||
|
self.check_with_txn(&txn, authcid, password)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn store(&self, txn: &mut RwTransaction, authcid: &str, password: &[u8]) -> Result<()> {
|
/// Store a password for a given authcid, potentially overwriting an existing password
|
||||||
|
pub fn store_with_txn(&self, txn: &mut RwTransaction, authcid: &str, password: &[u8]) -> Result<()> {
|
||||||
let config = argon2::Config::default();
|
let config = argon2::Config::default();
|
||||||
let salt: [u8; 16] = rand::random();
|
let salt: [u8; 16] = rand::random();
|
||||||
let hash = argon2::hash_encoded(password, &salt, &config)?;
|
let hash = argon2::hash_encoded(password, &salt, &config)?;
|
||||||
|
@ -9,8 +9,11 @@ use std::collections::HashMap;
|
|||||||
mod internal;
|
mod internal;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
/// An user
|
||||||
pub struct User {
|
pub struct User {
|
||||||
|
/// The precise (and unique) identifier of this user
|
||||||
pub id: UserId,
|
pub id: UserId,
|
||||||
|
/// Data BFFH stores on this user to base decisions on
|
||||||
pub data: UserData,
|
pub data: UserData,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,7 +23,7 @@ pub struct User {
|
|||||||
/// This identity is internal to FabAccess and completely independent from the authentication
|
/// This identity is internal to FabAccess and completely independent from the authentication
|
||||||
/// method or source
|
/// method or source
|
||||||
pub struct UserId {
|
pub struct UserId {
|
||||||
/// Main User ID. Generally an user name or similar
|
/// Main User ID. Generally an user name or similar. Locally unique
|
||||||
uid: String,
|
uid: String,
|
||||||
/// Sub user ID.
|
/// Sub user ID.
|
||||||
///
|
///
|
||||||
@ -55,7 +58,9 @@ impl fmt::Display for UserId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
||||||
/// A Person, from the Authorization perspective
|
/// Data on an user to base decisions on
|
||||||
|
///
|
||||||
|
/// This of course includes authorization data, i.e. that users set roles
|
||||||
pub struct UserData {
|
pub struct UserData {
|
||||||
/// A Person has N ≥ 0 roles.
|
/// A Person has N ≥ 0 roles.
|
||||||
/// Persons are only ever given roles, not permissions directly
|
/// Persons are only ever given roles, not permissions directly
|
||||||
|
@ -217,9 +217,12 @@ fn main() -> Result<(), Error> {
|
|||||||
let pdb = pdb?;
|
let pdb = pdb?;
|
||||||
let mut ac = db::access::AccessControl::new();
|
let mut ac = db::access::AccessControl::new();
|
||||||
ac.add_source_unchecked("Internal".to_string(), Box::new(pdb));
|
ac.add_source_unchecked("Internal".to_string(), Box::new(pdb));
|
||||||
|
|
||||||
|
let passdb = db::pass::PassDB::init(log.new(o!("system" => "passwords")), env.clone()).unwrap();
|
||||||
let db = db::Databases {
|
let db = db::Databases {
|
||||||
access: Arc::new(db::access::AccessControl::new()),
|
access: Arc::new(db::access::AccessControl::new()),
|
||||||
machine: Arc::new(machdb),
|
machine: Arc::new(machdb),
|
||||||
|
passdb: Arc::new(passdb),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Since the below closures will happen at a much later time we need to make sure all pointers
|
// Since the below closures will happen at a much later time we need to make sure all pointers
|
||||||
|
Loading…
Reference in New Issue
Block a user