Actually make compile for once.

This commit is contained in:
Gregor Reitzenstein 2020-11-24 15:57:23 +01:00
parent 5c5a59a75c
commit 7956616891
12 changed files with 196 additions and 40 deletions

View File

@ -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
View 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
// };
// }

View File

@ -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)
}
}
}

View File

@ -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) }
}

View File

@ -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)
}

View File

@ -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
View 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)
}
}

View File

@ -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
View 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(())
}
}

View File

@ -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)
}

View File

@ -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
View File