mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-22 14:57:56 +01:00
Status commit
This commit is contained in:
parent
9227b632e4
commit
b203edf206
995
Cargo.lock
generated
995
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -55,5 +55,10 @@ lmdb-rkv = "0.14"
|
||||
|
||||
async-trait = "0.1"
|
||||
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
rust-argon2 = "0.8"
|
||||
rand = "0.7"
|
||||
|
||||
[build-dependencies]
|
||||
capnpc = "0.13"
|
||||
|
@ -172,58 +172,34 @@ impl auth_capnp::authentication::Server for Auth {
|
||||
// somewhere and pass it somewhere else and in between don't check if it's the right type and
|
||||
// accidentally pass the authzid where the authcid should have gone.
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
/// Authentication Identity
|
||||
///
|
||||
/// Under the hood a string because the form depends heavily on the method
|
||||
struct AuthCId(String);
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
// What is a man?! A miserable little pile of secrets!
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
/// Authentication/Authorization user object.
|
||||
///
|
||||
/// This struct contains the user as is passed to the actual authentication/authorization
|
||||
/// subsystems
|
||||
/// This struct describes the user as can be gathered from API authentication exchanges.
|
||||
/// Specifically this is the value bffh gets after a successful authentication.
|
||||
///
|
||||
pub struct User {
|
||||
pub struct AuthenticationData {
|
||||
/// Contains the Authentication ID used
|
||||
///
|
||||
/// The authentication ID is an identifier for the authentication exchange. This is different
|
||||
/// than the ID of the user to be authenticated; for example when using x509 the authcid is
|
||||
/// the dn of the certificate, when using GSSAPI the authcid is of form `<userid>@<REALM>`
|
||||
authcid: AuthCId,
|
||||
/// The authentication ID is an identifier for the authentication exchange. This is
|
||||
/// conceptually different than the ID of the user to be authenticated; for example when using
|
||||
/// x509 the authcid is the dn of the certificate, when using GSSAPI the authcid is of form
|
||||
/// `<ID>@<REALM>`
|
||||
authcid: String,
|
||||
|
||||
/// Contains the Authorization ID
|
||||
/// Authorization ID
|
||||
///
|
||||
/// This is the identifier of the user to *authenticate as*. This in several cases is different
|
||||
/// to the `authcid`:
|
||||
/// The authzid represents the identity that a client wants to act as. In our case this is
|
||||
/// always an user id. If unset no preference is indicated and the server will authenticate the
|
||||
/// client as whatever user — if any — they associate with the authcid. Setting the authzid is
|
||||
/// useful in a number if situations:
|
||||
/// If somebody wants to authenticate as somebody else, su-style.
|
||||
/// If a person wants to authenticate as a higher-permissions account, e.g. foo may set authzid foo+admin
|
||||
/// to split normal user and "admin" accounts.
|
||||
/// If a method requires a specific authcid that is different from the identifier of the user
|
||||
/// to authenticate as, e.g. GSSAPI, x509 client certificates, API TOKEN authentication.
|
||||
authzid: AuthZId,
|
||||
authzid: String,
|
||||
|
||||
/// Contains the authentication method used
|
||||
///
|
||||
|
@ -13,20 +13,33 @@ use capnp_rpc::{twoparty, rpc_twoparty_capnp};
|
||||
use crate::schema::connection_capnp;
|
||||
|
||||
use crate::db::Databases;
|
||||
use crate::db::access::{AccessControl, Permission};
|
||||
use crate::builtin;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Connection context
|
||||
// TODO this should track over several connections
|
||||
pub struct Session {
|
||||
// Session-spezific log
|
||||
pub log: Logger,
|
||||
pub user: Option<auth::User>,
|
||||
authz_data: Option<AuthorizationContext>,
|
||||
accessdb: Arc<AccessControl>,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
pub fn new(log: Logger) -> Self {
|
||||
pub fn new(log: Logger, accessdb: Arc<AccessControl>) -> Self {
|
||||
let user = None;
|
||||
|
||||
Session { log, user }
|
||||
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.user.as_ref() {
|
||||
self.accessdb.check(user, perm).await
|
||||
} else {
|
||||
self.accessdb.check_roles(builtin::DEFAULT_ROLEIDS, perm).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,7 +74,7 @@ pub async fn handle_connection(log: Logger, mut stream: TcpStream, db: Databases
|
||||
handshake(&log, &mut stream).await?;
|
||||
|
||||
info!(log, "New connection from on {:?}", stream);
|
||||
let session = Arc::new(Session::new(log));
|
||||
let session = Arc::new(Session::new(log, db.access.clone()));
|
||||
let boots = Bootstrap::new(session, db);
|
||||
let rpc: connection_capnp::bootstrap::Client = capnp_rpc::new_client(boots);
|
||||
|
||||
|
@ -15,6 +15,9 @@ pub mod user;
|
||||
/// Stores&Retrieves Machines
|
||||
pub mod machine;
|
||||
|
||||
/// Authenticate users
|
||||
pub mod pass;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Databases {
|
||||
pub access: Arc<access::AccessControl>,
|
||||
|
@ -58,9 +58,33 @@ impl AccessControl {
|
||||
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
pub async fn check_roles<P: AsRef<Permission>>(&self, roles: &[RoleIdentifier], perm: &P)
|
||||
-> Result<bool>
|
||||
{
|
||||
for v in self.sources.values() {
|
||||
if v.check_roles(roles, perm.as_ref())? {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for AccessControl {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let b = f.debug_struct("AccessControl");
|
||||
for (name, roledb) in self.sources.iter() {
|
||||
b.field(name, &roledb.get_type_name().to_string());
|
||||
}
|
||||
b.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RoleDB {
|
||||
fn get_type_name(&self) -> &'static str;
|
||||
|
||||
fn get_role(&self, roleID: &RoleIdentifier) -> Result<Option<Role>>;
|
||||
|
||||
/// Check if a given user has the given permission
|
||||
@ -130,8 +154,6 @@ pub trait RoleDB {
|
||||
/// assign to all users.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Role {
|
||||
name: String,
|
||||
|
||||
// If a role doesn't define parents, default to an empty Vec.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
/// A Role can have parents, inheriting all permissions
|
||||
@ -328,6 +350,10 @@ impl PermissionBuf {
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
pub fn from_perm(perm: &Permission) -> Self {
|
||||
Self { inner: perm.inner.to_string() }
|
||||
}
|
||||
|
||||
pub fn into_string(self) -> String {
|
||||
self.inner
|
||||
}
|
||||
@ -368,7 +394,7 @@ pub struct Permission {
|
||||
inner: str
|
||||
}
|
||||
impl Permission {
|
||||
pub fn new<S: AsRef<str> + ?Sized>(s: &S) -> &Permission {
|
||||
pub const fn new<S: AsRef<str> + ?Sized>(s: &S) -> &Permission {
|
||||
unsafe { &*(s.as_ref() as *const str as *const Permission) }
|
||||
}
|
||||
|
||||
|
@ -150,6 +150,10 @@ impl Internal {
|
||||
}
|
||||
|
||||
impl RoleDB for Internal {
|
||||
fn get_type_name(&self) -> &'static str {
|
||||
"Internal"
|
||||
}
|
||||
|
||||
fn check(&self, user: &User, perm: &Permission) -> Result<bool> {
|
||||
let txn = self.env.begin_ro_txn()?;
|
||||
self._check(&txn, user, &perm)
|
||||
|
@ -5,7 +5,7 @@ use std::collections::HashMap;
|
||||
|
||||
/// A Person, from the Authorization perspective
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct User {
|
||||
pub struct AuthzContext {
|
||||
/// The identification of this user.
|
||||
pub id: UserIdentifier,
|
||||
|
||||
@ -18,37 +18,16 @@ pub struct User {
|
||||
kv: HashMap<Box<[u8]>, Box<[u8]>>,
|
||||
}
|
||||
|
||||
|
||||
/// Locally unique identifier for an user
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct UserIdentifier {
|
||||
/// Main UID. Must be unique in this instance so that the tuple (uid, location) is globally
|
||||
/// unique.
|
||||
uid: String,
|
||||
/// Subordinate ID. Must be unique for this user, i.e. the tuple (uid, subuid) must be unique
|
||||
/// but two different uids can have the same subuid. `None` means no subuid is set and the ID
|
||||
/// refers to the main users
|
||||
subuid: Option<String>,
|
||||
/// Location of the instance the user comes from. `None` means the local instance.
|
||||
location: Option<String>,
|
||||
}
|
||||
|
||||
impl UserIdentifier {
|
||||
pub fn new(uid: String, subuid: Option<String>, location: Option<String>) -> Self {
|
||||
Self { uid, subuid, location }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UserIdentifier {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let r = write!(f, "{}", self.uid);
|
||||
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)?;
|
||||
}
|
||||
r
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,24 +35,3 @@ impl fmt::Display for UserIdentifier {
|
||||
pub trait UserDB {
|
||||
fn get_user(&self, uid: UserIdentifier) -> Option<User>;
|
||||
}
|
||||
|
||||
#[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))));
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ pub enum Error {
|
||||
MQTT(mqtt::Error),
|
||||
Config(config::ConfigError),
|
||||
BadVersion((u32,u32)),
|
||||
Argon2(argon2::Error)
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
@ -144,4 +145,10 @@ impl From<config::ConfigError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<argon2::Error> for Error {
|
||||
fn from(e: argon2::Error) -> Error {
|
||||
Error::Argon2(e)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type Result<T> = std::result::Result<T, Error>;
|
||||
|
@ -17,6 +17,7 @@ mod registries;
|
||||
mod schema;
|
||||
mod db;
|
||||
mod machine;
|
||||
mod builtin;
|
||||
|
||||
use clap::{App, Arg};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user