diff --git a/src/api.rs b/src/api.rs index 54a2a97..ca51fa4 100644 --- a/src/api.rs +++ b/src/api.rs @@ -9,6 +9,8 @@ use crate::connection::Session; use crate::db::Databases; +use crate::builtin; + pub mod auth; mod machine; mod machines; @@ -36,11 +38,18 @@ 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)? - if self.session.user.is_none() { - res.get().set_auth(capnp_rpc::new_client(auth::Auth::new(self.session.clone()))) - } + 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()))) + } - Promise::ok(()) + Ok(()) + }; + + Promise::from_future(f) } fn permissions(&mut self, diff --git a/src/connection.rs b/src/connection.rs index 9edf831..74be330 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -14,6 +14,7 @@ use crate::schema::connection_capnp; use crate::db::Databases; use crate::db::access::{AccessControl, Permission}; +use crate::db::user::AuthzContext; use crate::builtin; #[derive(Debug, Clone)] @@ -22,20 +23,20 @@ use crate::builtin; pub struct Session { // Session-spezific log pub log: Logger, - authz_data: Option, + authz_data: Option, accessdb: Arc, } impl Session { pub fn new(log: Logger, accessdb: Arc) -> Self { - let user = None; + let authz_data = None; - Session { log, user, accessdb } + Session { log, authz_data, accessdb } } /// Check if the current session has a certain permission pub async fn check_permission>(&self, perm: &P) -> Result { - if let Some(user) = self.user.as_ref() { + if let Some(user) = self.authz_data.as_ref() { self.accessdb.check(user, perm).await } else { self.accessdb.check_roles(builtin::DEFAULT_ROLEIDS, perm).await diff --git a/src/db/access.rs b/src/db/access.rs index 41c5fc4..71ceec5 100644 --- a/src/db/access.rs +++ b/src/db/access.rs @@ -29,7 +29,7 @@ use crate::error::Result; pub mod internal; -use crate::db::user::User; +use crate::db::user::AuthzContext; pub use internal::init; pub struct AccessControl { @@ -49,7 +49,7 @@ impl AccessControl { self.sources.insert(name, source); } - pub async fn check>(&self, user: &User, perm: &P) -> Result { + pub async fn check>(&self, user: &AuthzContext, perm: &P) -> Result { for v in self.sources.values() { if v.check(user, perm.as_ref())? { return Ok(true); @@ -91,7 +91,7 @@ pub trait RoleDB { /// /// Default implementation which adapter may overwrite with more efficient specialized /// implementations. - fn check(&self, user: &User, perm: &Permission) -> Result { + fn check(&self, user: &AuthzContext, perm: &Permission) -> Result { self.check_roles(&user.roles, perm) } diff --git a/src/db/access/internal.rs b/src/db/access/internal.rs index 1327e65..d988e66 100644 --- a/src/db/access/internal.rs +++ b/src/db/access/internal.rs @@ -17,7 +17,7 @@ use crate::config::Settings; use crate::error::Result; use crate::db::access::{Permission, Role, RoleIdentifier, RoleDB}; -use crate::db::user::{UserIdentifier, User}; +use crate::db::user::AuthzContext; #[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>(&self, txn: &T, user: &User, perm: &P) + pub fn _check>(&self, txn: &T, user: &AuthzContext, perm: &P) -> Result { // Tally all roles. Makes dependent roles easier @@ -154,7 +154,7 @@ impl RoleDB for Internal { "Internal" } - fn check(&self, user: &User, perm: &Permission) -> Result { + fn check(&self, user: &AuthzContext, perm: &Permission) -> Result { let txn = self.env.begin_ro_txn()?; self._check(&txn, user, &perm) } diff --git a/src/db/machine.rs b/src/db/machine.rs index c94a179..77b4d1f 100644 --- a/src/db/machine.rs +++ b/src/db/machine.rs @@ -16,8 +16,6 @@ use crate::error::Result; use crate::config::Settings; use crate::db::access; -use crate::db::user::UserIdentifier; - use capnp::Error; use uuid::Uuid; @@ -30,7 +28,6 @@ use futures::{Future, Stream, StreamExt}; use futures_signals::signal::*; use crate::registries::StatusSignal; -use crate::db::user::User; use crate::machine::MachineDescription; diff --git a/src/db/user.rs b/src/db/user.rs index 4b7b928..e624025 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -3,11 +3,32 @@ use std::fmt; use crate::db::access::RoleIdentifier; use std::collections::HashMap; +#[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 { + /// Main User ID. Generally an user name or similar + uid: String, + /// Sub user ID. + /// + /// 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, + /// Realm this account originates. + /// + /// The Realm is usually described by a domain name but local policy may dictate an unrelated + /// mapping + realm: String, +} + /// A Person, from the Authorization perspective #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] pub struct AuthzContext { /// The identification of this user. - pub id: UserIdentifier, + pub id: AuthZId, /// A Person has N ≥ 0 roles. /// Persons are only ever given roles, not permissions directly @@ -18,20 +39,23 @@ pub struct AuthzContext { kv: HashMap, Box<[u8]>>, } -impl fmt::Display for UserIdentifier { - 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.location { - write!(f, "@{}", l)?; - } - Ok(r) +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn format_uid_test() { + let uid = "testuser".to_string(); + let suid = "testsuid".to_string(); + let location = "testloc".to_string(); + + assert_eq!("testuser", + format!("{}", UserIdentifier::new(uid.clone(), None, None))); + assert_eq!("testuser+testsuid", + format!("{}", UserIdentifier::new(uid.clone(), Some(suid.clone()), None))); + 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)))); } } - -/// User Database Trait -pub trait UserDB { - fn get_user(&self, uid: UserIdentifier) -> Option; -} diff --git a/src/machine.rs b/src/machine.rs index cec9211..87463ad 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -12,7 +12,6 @@ use uuid::Uuid; use crate::error::Result; -use crate::db::user::User; use crate::db::access; use crate::db::machine::{MachineIdentifier, Status, MachineState};