@@ -0,0 +1,161 @@
|
||||
//! User authentication.
|
||||
|
||||
use bitflags::bitflags;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
macro_rules! decl_session_args {
|
||||
(pub struct $sn:ident { $(pub $n:ident : $t:ty,)* }) => {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct $sn {
|
||||
$(pub $n: $t),*
|
||||
}
|
||||
impl $sn {
|
||||
pub fn parse(args: &[String]) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
Self::_from_session_args(SessionArgs::parse(args)?)
|
||||
}
|
||||
|
||||
pub fn compose(&self) -> Vec<String> {
|
||||
self._to_session_args().compose()
|
||||
}
|
||||
|
||||
fn _from_session_args(sa: SessionArgs) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
Ok(Self {
|
||||
$(
|
||||
$n: sa.0.get(stringify!($n))
|
||||
.ok_or_else(|| Box::<dyn std::error::Error>::from(format!(
|
||||
"argument {} not found", stringify!($n),
|
||||
)))?
|
||||
.parse::<$t>()?
|
||||
),*
|
||||
})
|
||||
}
|
||||
|
||||
fn _to_session_args(&self) -> SessionArgs {
|
||||
let mut map = HashMap::new();
|
||||
$(
|
||||
map.insert(stringify!($n).into(), self.$n.to_string());
|
||||
)*
|
||||
SessionArgs(map)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Maximum count of auth methods enabled for one user.
|
||||
pub const USER_MAX_AUTH_METHODS: usize = 32;
|
||||
|
||||
/// Record of an auth method enabled for a user.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UserAuthMethodRecord {
|
||||
/// Name of the auth method.
|
||||
pub name: String,
|
||||
|
||||
/// Flags of the auth method.
|
||||
pub flags: UserAuthMethodFlags,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct UserAuthMethodFlags: u32 {
|
||||
/// Indicates if the auth method is a "main" auth method.
|
||||
///
|
||||
/// A "main" auth method is unique for a user. It provides a master key for secret storage.
|
||||
const MAIN = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about an authentication method.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AuthMethodInfo {
|
||||
/// Name of the auth method.
|
||||
pub name: String,
|
||||
|
||||
/// True if the auth method supports providing the master key.
|
||||
pub provides_master_key: bool,
|
||||
|
||||
/// Supported operation modes.
|
||||
pub modes: Vec<ModeInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ModeInfo {
|
||||
/// Name of the operation mode.
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// Command line of an authentication method provider.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AuthCli {
|
||||
/// Queries information about the auth method.
|
||||
///
|
||||
/// The implementor should output an [`AuthMethod`] in JSON to stdout stream, and then exit.
|
||||
QueryInformation,
|
||||
|
||||
/// Begins an authentication session.
|
||||
Authenticate(AuthenticateArgs),
|
||||
|
||||
/// Begins an update session.
|
||||
Update(UpdateArgs),
|
||||
}
|
||||
impl AuthCli {
|
||||
pub fn parse(args: &[String]) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
match args.first().map(String::as_str) {
|
||||
Some("QueryInformation") => Ok(Self::QueryInformation),
|
||||
Some("Authenticate") => Ok(Self::Authenticate(AuthenticateArgs::parse(&args[1..])?)),
|
||||
Some("Update") => Ok(Self::Update(UpdateArgs::parse(&args[1..])?)),
|
||||
Some(unexpected) => Err(Box::from(format!("unexpected command `{unexpected}`"))),
|
||||
None => Err(Box::from("no command specified")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compose(&self) -> Vec<String> {
|
||||
match self {
|
||||
Self::QueryInformation => vec!["QueryInformation".into()],
|
||||
Self::Authenticate(args) => {
|
||||
let mut ret = vec!["Authenticate".into()];
|
||||
ret.append(&mut args.compose());
|
||||
ret
|
||||
}
|
||||
Self::Update(args) => {
|
||||
let mut ret = vec!["Update".into()];
|
||||
ret.append(&mut args.compose());
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decl_session_args! {
|
||||
pub struct AuthenticateArgs {
|
||||
pub user_uuid: Uuid,
|
||||
pub mode: String,
|
||||
}
|
||||
}
|
||||
|
||||
decl_session_args! {
|
||||
pub struct UpdateArgs {
|
||||
pub user_uuid: Uuid,
|
||||
pub mode: String,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct SessionArgs(HashMap<String, String>);
|
||||
impl SessionArgs {
|
||||
fn parse(args: &[String]) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let mut map = HashMap::with_capacity(args.len());
|
||||
for arg in args {
|
||||
let Some((key, val)) = arg.split_once('=') else {
|
||||
return Err(Box::from("invalid argument format"));
|
||||
};
|
||||
map.insert(key.into(), val.into());
|
||||
}
|
||||
Ok(Self(map))
|
||||
}
|
||||
|
||||
fn compose(&self) -> Vec<String> {
|
||||
self.0.iter().map(|(k, v)| format!("{k}={v}")).collect()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user