netidx 0.31.8

Secure, fast, pub/sub messaging
Documentation
use super::{
    auth::{PMap, UserDb},
    config::{Auth, Config, MemberServer},
};
use crate::{
    channel::K5CtxWrap,
    os::{
        local_auth::{AuthServer, Credential},
        Mapper,
    },
    protocol::resolver::PublisherId,
    tls,
};
use anyhow::{bail, Result};
use arcstr::ArcStr;
use cross_krb5::{K5Ctx, ServerCtx};
use fxhash::FxHashMap;
use log::debug;
use netidx_core::pack::Pack;
use std::{collections::HashMap, sync::Arc};
use tokio::sync::{RwLock, RwLockReadGuard};

pub(super) struct LocalAuth(AuthServer);

impl LocalAuth {
    pub(super) async fn new(
        socket_path: &str,
        cfg: &Config,
        member: &MemberServer,
    ) -> Result<Self> {
        Ok(Self(AuthServer::start(socket_path, cfg, member).await?))
    }

    pub(super) fn authenticate(&self, mut token: &[u8]) -> Result<Credential> {
        if token.len() < 10 {
            bail!("token short")
        }
        let cred = <Credential as Pack>::decode(&mut token)?;
        if !self.0.validate(&cred) {
            bail!("invalid token")
        }
        Ok(cred)
    }
}

pub(super) trait SecDataCommon {
    fn secret(&self) -> u128;
}

pub(super) struct SecCtxData<S: 'static> {
    pub(super) users: UserDb,
    pub(super) pmap: PMap,
    data: FxHashMap<PublisherId, S>,
}

impl<S: 'static + SecDataCommon> SecCtxData<S> {
    pub(super) async fn new(cfg: &Config, member: &MemberServer) -> Result<Self> {
        let mut users =
            UserDb::new(member.id_map_timeout, Mapper::new(cfg, member).await?);
        let pmap = PMap::from_file(&cfg.perms, &mut users, cfg.root(), &cfg.children)?;
        Ok(Self { users, pmap, data: HashMap::default() })
    }

    pub(super) fn remove(&mut self, id: &PublisherId) {
        self.data.remove(&id);
    }

    pub(super) fn insert(&mut self, id: PublisherId, data: S) {
        self.remove(&id);
        self.data.insert(id, data);
    }

    pub(super) fn secret(&self, id: &PublisherId) -> Option<u128> {
        self.data.get(id).map(|d| d.secret())
    }
}

#[derive(Debug, Clone)]
pub(super) struct K5SecData {
    pub(super) secret: u128,
    pub(super) ctx: K5CtxWrap<ServerCtx>,
}

impl SecDataCommon for K5SecData {
    fn secret(&self) -> u128 {
        self.secret
    }
}

impl SecCtxData<K5SecData> {
    pub(super) fn get(&self, id: &PublisherId) -> Option<&K5SecData> {
        self.data.get(id).and_then(|r| match r.ctx.lock().ttl() {
            Ok(ttl) if ttl.as_secs() > 0 => Some(r),
            _ => None,
        })
    }
}

#[derive(Debug, Clone)]
pub(super) struct LocalSecData {
    pub(super) user: ArcStr,
    pub(super) secret: u128,
}

impl SecDataCommon for LocalSecData {
    fn secret(&self) -> u128 {
        self.secret
    }
}

impl SecCtxData<LocalSecData> {
    pub(super) fn get(&self, id: &PublisherId) -> Option<&LocalSecData> {
        self.data.get(id)
    }
}

#[derive(Debug, Clone, Copy)]
pub(super) struct TlsSecData(pub(super) u128);

impl SecDataCommon for TlsSecData {
    fn secret(&self) -> u128 {
        self.0
    }
}

impl SecCtxData<TlsSecData> {
    pub(super) fn get(&self, id: &PublisherId) -> Option<&TlsSecData> {
        self.data.get(id)
    }
}

pub(super) enum SecCtxDataReadGuard<'a> {
    Anonymous,
    Krb5(RwLockReadGuard<'a, SecCtxData<K5SecData>>),
    Local(RwLockReadGuard<'a, SecCtxData<LocalSecData>>),
    Tls(RwLockReadGuard<'a, SecCtxData<TlsSecData>>),
}

impl<'a> SecCtxDataReadGuard<'a> {
    pub(super) fn pmap(&'a self) -> Option<&'a PMap> {
        match self {
            SecCtxDataReadGuard::Anonymous => None,
            SecCtxDataReadGuard::Krb5(r) => Some(&r.pmap),
            SecCtxDataReadGuard::Local(r) => Some(&r.pmap),
            SecCtxDataReadGuard::Tls(r) => Some(&r.pmap),
        }
    }
}

#[derive(Clone)]
pub(super) enum SecCtx {
    Anonymous,
    Krb5(Arc<(ArcStr, RwLock<SecCtxData<K5SecData>>)>),
    Local(Arc<(LocalAuth, RwLock<SecCtxData<LocalSecData>>)>),
    Tls(Arc<(tokio_rustls::TlsAcceptor, RwLock<SecCtxData<TlsSecData>>)>),
}

impl SecCtx {
    pub(super) async fn new(cfg: &Config, member: &MemberServer) -> Result<Self> {
        let t = match &member.auth {
            Auth::Anonymous => SecCtx::Anonymous,
            Auth::Local { path } => {
                debug!("starting local authenticator process");
                let auth = LocalAuth::new(&path, cfg, member).await?;
                let store = RwLock::new(SecCtxData::new(cfg, member).await?);
                SecCtx::Local(Arc::new((auth, store)))
            }
            Auth::Krb5 { spn } => {
                debug!("creating kerberos context with spn {}", spn);
                let store = RwLock::new(SecCtxData::new(cfg, member).await?);
                SecCtx::Krb5(Arc::new((spn.clone(), store)))
            }
            Auth::Tls { name: _, trusted, certificate, private_key } => {
                debug!("creating tls acceptor");
                let auth =
                    tls::create_tls_acceptor(None, trusted, certificate, private_key)?;
                let store = RwLock::new(SecCtxData::new(cfg, member).await?);
                SecCtx::Tls(Arc::new((auth, store)))
            }
        };
        Ok(t)
    }

    pub(super) async fn read<'a>(&'a self) -> SecCtxDataReadGuard<'a> {
        match self {
            SecCtx::Anonymous => SecCtxDataReadGuard::Anonymous,
            SecCtx::Krb5(a) => SecCtxDataReadGuard::Krb5(a.1.read().await),
            SecCtx::Local(a) => SecCtxDataReadGuard::Local(a.1.read().await),
            SecCtx::Tls(a) => SecCtxDataReadGuard::Tls(a.1.read().await),
        }
    }

    pub(super) async fn remove(&self, id: &PublisherId) {
        match self {
            SecCtx::Krb5(a) => a.1.write().await.remove(id),
            SecCtx::Local(a) => a.1.write().await.remove(id),
            SecCtx::Tls(a) => a.1.write().await.remove(id),
            SecCtx::Anonymous => (),
        }
    }
}