mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-12-22 19:53:49 +01:00
Actually make compile for once.
This commit is contained in:
parent
5c5a59a75c
commit
7956616891
12
src/api.rs
12
src/api.rs
@ -38,18 +38,10 @@ impl connection_capnp::bootstrap::Server for Bootstrap {
|
||||
// Forbid mutltiple authentication for now
|
||||
// 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)?
|
||||
let session = self.session.clone();
|
||||
let check_perm_future = session.check_permission(&builtin::AUTH_PERM);
|
||||
let f = async {
|
||||
let r = check_perm_future.await.unwrap();
|
||||
if r {
|
||||
res.get().set_auth(capnp_rpc::new_client(auth::Auth::new(session.clone())))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
res.get().set_auth(capnp_rpc::new_client(auth::Auth::new(self.session.clone())));
|
||||
|
||||
Promise::from_future(f)
|
||||
Promise::ok(())
|
||||
}
|
||||
|
||||
fn permissions(&mut self,
|
||||
|
42
src/builtin.rs
Normal file
42
src/builtin.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use std::collections::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use crate::db::access::{
|
||||
Permission,
|
||||
PermissionBuf,
|
||||
PermRule,
|
||||
RoleIdentifier,
|
||||
Role,
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
static ref AUTH_PERM: &'static Permission = Permission::new("bffh.auth");
|
||||
}
|
||||
|
||||
//
|
||||
// lazy_static! {
|
||||
// pub static ref AUTH_ROLE: RoleIdentifier = {
|
||||
// RoleIdentifier::Local {
|
||||
// name: "mayauth".to_string(),
|
||||
// source: "builtin".to_string(),
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// lazy_static! {
|
||||
// pub static ref DEFAULT_ROLEIDS: [RoleIdentifier; 1] = {
|
||||
// [ AUTH_ROLE.clone(), ]
|
||||
// };
|
||||
//
|
||||
// pub static ref DEFAULT_ROLES: HashMap<RoleIdentifier, Role> = {
|
||||
// let mut m = HashMap::new();
|
||||
// m.insert(AUTH_ROLE.clone(),
|
||||
// Role {
|
||||
// parents: vec![],
|
||||
// permissions: vec![
|
||||
// PermRule::Base(PermissionBuf::from_perm(AUTH_PERM)),
|
||||
// ]
|
||||
// }
|
||||
// );
|
||||
// m
|
||||
// };
|
||||
// }
|
@ -14,7 +14,7 @@ use crate::schema::connection_capnp;
|
||||
|
||||
use crate::db::Databases;
|
||||
use crate::db::access::{AccessControl, Permission};
|
||||
use crate::db::user::AuthzContext;
|
||||
use crate::db::user::User;
|
||||
use crate::builtin;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -23,23 +23,23 @@ use crate::builtin;
|
||||
pub struct Session {
|
||||
// Session-spezific log
|
||||
pub log: Logger,
|
||||
authz_data: Option<AuthzContext>,
|
||||
user: Option<User>,
|
||||
accessdb: Arc<AccessControl>,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
pub fn new(log: Logger, accessdb: Arc<AccessControl>) -> Self {
|
||||
let authz_data = None;
|
||||
let user = None;
|
||||
|
||||
Session { log, authz_data, accessdb }
|
||||
Session { log, user, accessdb }
|
||||
}
|
||||
|
||||
/// Check if the current session has a certain permission
|
||||
pub async fn check_permission<P: AsRef<Permission>>(&self, perm: &P) -> Result<bool> {
|
||||
if let Some(user) = self.authz_data.as_ref() {
|
||||
self.accessdb.check(user, perm).await
|
||||
if let Some(user) = self.user.as_ref() {
|
||||
self.accessdb.check(&user.data, perm).await
|
||||
} else {
|
||||
self.accessdb.check_roles(builtin::DEFAULT_ROLEIDS, perm).await
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ use crate::error::Result;
|
||||
|
||||
pub mod internal;
|
||||
|
||||
use crate::db::user::AuthzContext;
|
||||
use crate::db::user::UserData;
|
||||
pub use internal::init;
|
||||
|
||||
pub struct AccessControl {
|
||||
@ -49,7 +49,7 @@ impl AccessControl {
|
||||
self.sources.insert(name, source);
|
||||
}
|
||||
|
||||
pub async fn check<P: AsRef<Permission>>(&self, user: &AuthzContext, perm: &P) -> Result<bool> {
|
||||
pub async fn check<P: AsRef<Permission>>(&self, user: &UserData, perm: &P) -> Result<bool> {
|
||||
for v in self.sources.values() {
|
||||
if v.check(user, perm.as_ref())? {
|
||||
return Ok(true);
|
||||
@ -74,7 +74,7 @@ impl AccessControl {
|
||||
|
||||
impl fmt::Debug for AccessControl {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let b = f.debug_struct("AccessControl");
|
||||
let mut b = f.debug_struct("AccessControl");
|
||||
for (name, roledb) in self.sources.iter() {
|
||||
b.field(name, &roledb.get_type_name().to_string());
|
||||
}
|
||||
@ -91,7 +91,7 @@ pub trait RoleDB {
|
||||
///
|
||||
/// Default implementation which adapter may overwrite with more efficient specialized
|
||||
/// implementations.
|
||||
fn check(&self, user: &AuthzContext, perm: &Permission) -> Result<bool> {
|
||||
fn check(&self, user: &UserData, perm: &Permission) -> Result<bool> {
|
||||
self.check_roles(&user.roles, perm)
|
||||
}
|
||||
|
||||
@ -394,7 +394,7 @@ pub struct Permission {
|
||||
inner: str
|
||||
}
|
||||
impl Permission {
|
||||
pub const fn new<S: AsRef<str> + ?Sized>(s: &S) -> &Permission {
|
||||
pub fn new<S: AsRef<str> + ?Sized>(s: &S) -> &Permission {
|
||||
unsafe { &*(s.as_ref() as *const str as *const Permission) }
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ use crate::config::Settings;
|
||||
use crate::error::Result;
|
||||
|
||||
use crate::db::access::{Permission, Role, RoleIdentifier, RoleDB};
|
||||
use crate::db::user::AuthzContext;
|
||||
use crate::db::user::{User, UserData};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Internal {
|
||||
@ -34,7 +34,7 @@ impl Internal {
|
||||
|
||||
/// Check if a given user has the given permission
|
||||
#[allow(unused)]
|
||||
pub fn _check<T: Transaction, P: AsRef<Permission>>(&self, txn: &T, user: &AuthzContext, perm: &P)
|
||||
pub fn _check<T: Transaction, P: AsRef<Permission>>(&self, txn: &T, user: &UserData, perm: &P)
|
||||
-> Result<bool>
|
||||
{
|
||||
// Tally all roles. Makes dependent roles easier
|
||||
@ -154,7 +154,7 @@ impl RoleDB for Internal {
|
||||
"Internal"
|
||||
}
|
||||
|
||||
fn check(&self, user: &AuthzContext, perm: &Permission) -> Result<bool> {
|
||||
fn check(&self, user: &UserData, perm: &Permission) -> Result<bool> {
|
||||
let txn = self.env.begin_ro_txn()?;
|
||||
self._check(&txn, user, &perm)
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ use crate::registries::StatusSignal;
|
||||
|
||||
use crate::machine::MachineDescription;
|
||||
|
||||
use crate::db::user::UserId;
|
||||
|
||||
pub mod internal;
|
||||
use internal::Internal;
|
||||
|
||||
@ -42,15 +44,15 @@ pub enum Status {
|
||||
/// Not currently used by anybody
|
||||
Free,
|
||||
/// Used by somebody
|
||||
InUse(UserIdentifier),
|
||||
InUse(UserId),
|
||||
/// Was used by somebody and now needs to be checked for cleanliness
|
||||
ToCheck(UserIdentifier),
|
||||
ToCheck(UserId),
|
||||
/// Not used by anybody but also can not be used. E.g. down for maintenance
|
||||
Blocked(UserIdentifier),
|
||||
Blocked(UserId),
|
||||
/// Disabled for some other reason
|
||||
Disabled,
|
||||
/// Reserved
|
||||
Reserved(UserIdentifier),
|
||||
Reserved(UserId),
|
||||
}
|
||||
|
||||
pub fn uuid_from_api(uuid: crate::schema::api_capnp::u_u_i_d::Reader) -> Uuid {
|
||||
|
40
src/db/pass.rs
Normal file
40
src/db/pass.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use argon2;
|
||||
use lmdb::{Environment, Transaction, RwTransaction, Cursor};
|
||||
use rand::prelude::*;
|
||||
use slog::Logger;
|
||||
|
||||
use crate::error::Result;
|
||||
|
||||
pub struct PassDB {
|
||||
log: Logger,
|
||||
env: Arc<Environment>,
|
||||
db: lmdb::Database,
|
||||
}
|
||||
|
||||
impl PassDB {
|
||||
pub fn new(log: Logger, env: Arc<Environment>, db: lmdb::Database) -> Self {
|
||||
Self { log, env, db }
|
||||
}
|
||||
|
||||
pub fn check<T: Transaction>(&self, txn: &T, authcid: &str, password: &[u8]) -> Result<Option<bool>> {
|
||||
match txn.get(self.db, &authcid.as_bytes()) {
|
||||
Ok(bytes) => {
|
||||
let encoded = unsafe { std::str::from_utf8_unchecked(bytes) };
|
||||
let res = argon2::verify_encoded(encoded, password)?;
|
||||
Ok(Some(res))
|
||||
},
|
||||
Err(lmdb::Error::NotFound) => { Ok(None) },
|
||||
Err(e) => { Err(e.into()) },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store(&self, txn: &mut RwTransaction, authcid: &str, password: &[u8]) -> Result<()> {
|
||||
let config = argon2::Config::default();
|
||||
let salt: [u8; 16] = rand::random();
|
||||
let hash = argon2::hash_encoded(password, &salt, &config)?;
|
||||
txn.put(self.db, &authcid.as_bytes(), &hash.as_bytes(), lmdb::WriteFlags::empty())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
@ -1,14 +1,25 @@
|
||||
//! UserDB does two kinds of lookups:
|
||||
//! 1. "I have this here username, what user is that"
|
||||
//! 2. "I have this here user, what are their roles (and other associated data)"
|
||||
use serde::{Serialize, Deserialize};
|
||||
use std::fmt;
|
||||
use crate::db::access::RoleIdentifier;
|
||||
use std::collections::HashMap;
|
||||
|
||||
mod internal;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct User {
|
||||
pub id: UserId,
|
||||
pub data: UserData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
/// Authorization Identity
|
||||
///
|
||||
/// This identity is internal to FabAccess and completely independent from the authentication
|
||||
/// method or source
|
||||
struct AuthZId {
|
||||
pub struct UserId {
|
||||
/// Main User ID. Generally an user name or similar
|
||||
uid: String,
|
||||
/// Sub user ID.
|
||||
@ -16,20 +27,36 @@ struct AuthZId {
|
||||
/// Can change scopes for permissions, e.g. having a +admin account with more permissions than
|
||||
/// the default account and +dashboard et.al. accounts that have restricted permissions for
|
||||
/// their applications
|
||||
subuid: String,
|
||||
subuid: Option<String>,
|
||||
/// Realm this account originates.
|
||||
///
|
||||
/// The Realm is usually described by a domain name but local policy may dictate an unrelated
|
||||
/// mapping
|
||||
realm: String,
|
||||
realm: Option<String>,
|
||||
}
|
||||
|
||||
/// A Person, from the Authorization perspective
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AuthzContext {
|
||||
/// The identification of this user.
|
||||
pub id: AuthZId,
|
||||
impl UserId {
|
||||
pub fn new(uid: String, subuid: Option<String>, realm: Option<String>) -> Self {
|
||||
Self { uid, subuid, realm }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UserId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let r = write!(f, "{}", self.uid);
|
||||
if let Some(ref s) = self.subuid {
|
||||
write!(f, "+{}", s)?;
|
||||
}
|
||||
if let Some(ref l) = self.realm {
|
||||
write!(f, "@{}", l)?;
|
||||
}
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
||||
/// A Person, from the Authorization perspective
|
||||
pub struct UserData {
|
||||
/// A Person has N ≥ 0 roles.
|
||||
/// Persons are only ever given roles, not permissions directly
|
||||
pub roles: Vec<RoleIdentifier>,
|
||||
@ -47,7 +74,7 @@ mod tests {
|
||||
fn format_uid_test() {
|
||||
let uid = "testuser".to_string();
|
||||
let suid = "testsuid".to_string();
|
||||
let location = "testloc".to_string();
|
||||
let realm = "testloc".to_string();
|
||||
|
||||
assert_eq!("testuser",
|
||||
format!("{}", UserIdentifier::new(uid.clone(), None, None)));
|
||||
@ -56,6 +83,6 @@ mod tests {
|
||||
assert_eq!("testuser+testsuid",
|
||||
format!("{}", UserIdentifier::new(uid.clone(), Some(suid.clone()), None)));
|
||||
assert_eq!("testuser+testsuid@testloc",
|
||||
format!("{}", UserIdentifier::new(uid, Some(suid), Some(location))));
|
||||
format!("{}", UserIdentifier::new(uid, Some(suid), Some(realm))));
|
||||
}
|
||||
}
|
||||
|
49
src/db/user/internal.rs
Normal file
49
src/db/user/internal.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use slog::Logger;
|
||||
use lmdb::{Environment, Transaction, RwTransaction, Cursor};
|
||||
|
||||
use crate::error::Result;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Internal {
|
||||
log: Logger,
|
||||
env: Arc<Environment>,
|
||||
db: lmdb::Database,
|
||||
}
|
||||
|
||||
impl Internal {
|
||||
pub fn new(log: Logger, env: Arc<Environment>, db: lmdb::Database) -> Self {
|
||||
Self { log, env, db }
|
||||
}
|
||||
|
||||
pub fn get_user_txn<T: Transaction>(&self, txn: &T, uid: &str) -> Result<Option<User>> {
|
||||
match txn.get(self.db, &uid.as_bytes()) {
|
||||
Ok(bytes) => {
|
||||
Ok(Some(flexbuffers::from_slice(bytes)?))
|
||||
},
|
||||
Err(lmdb::Error::NotFound) => Ok(None),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
pub fn get_user(&self, uid: &str) -> Result<Option<User>> {
|
||||
let txn = self.env.begin_ro_txn()?;
|
||||
self.get_user_txn(&txn, uid)
|
||||
}
|
||||
|
||||
pub fn put_user_txn(&self, txn: &mut RwTransaction, uid: &str, user: &User) -> Result<()> {
|
||||
let bytes = flexbuffers::to_vec(user)?;
|
||||
txn.put(self.db, &uid.as_bytes(), &bytes, lmdb::WriteFlags::empty())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn put_user(&self, uid: &str, user: &User) -> Result<()> {
|
||||
let mut txn = self.env.begin_rw_txn()?;
|
||||
self.put_user_txn(&mut txn, uid, user)?;
|
||||
txn.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -66,6 +66,9 @@ impl fmt::Display for Error {
|
||||
Error::Config(e) => {
|
||||
write!(f, "Failed to parse config: {}", e)
|
||||
}
|
||||
Error::Argon2(e) => {
|
||||
write!(f, "Argon2 en/decoding failure: {}", e)
|
||||
}
|
||||
Error::BadVersion((major,minor)) => {
|
||||
write!(f, "Peer uses API version {}.{} which is incompatible!", major, minor)
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ use crate::error::Result;
|
||||
|
||||
use crate::db::access;
|
||||
use crate::db::machine::{MachineIdentifier, Status, MachineState};
|
||||
use crate::db::user::User;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Internal machine representation
|
||||
@ -66,7 +67,7 @@ impl Machine {
|
||||
) -> Result<bool>
|
||||
{
|
||||
// TODO: Check different levels
|
||||
if access.check(who, &self.desc.privs.write).await? {
|
||||
if access.check(&who.data, &self.desc.privs.write).await? {
|
||||
self.state.set(MachineState { state: Status::InUse(who.id.clone()) });
|
||||
return Ok(true);
|
||||
} else {
|
||||
|
0
src/user.rs
Normal file
0
src/user.rs
Normal file
Loading…
Reference in New Issue
Block a user